npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@compound-finance/quark

v0.0.6-alpha37

Published

The Quark client is a JavaScript library to quicky and easily build and invoke actions from Quark wallets.

Downloads

99

Readme

Quark Client

The Quark client is a JavaScript library to quicky and easily build and invoke actions from Quark wallets. The goal of this library is to cover a large subset of actions one might want to take when using a Quark wallet, from simply wrapping Ethers transactions, to building compositions of built-in DeFi functions, to running Solidity code.

Getting Started

Start by installing this library:

npm install --save @compound-finance/quark

# yarn add @compound-finance/quark

Many commands will require a Web3 Provider, e.g. from Ethers. We will automatically detect the network and find the correct Quark Relayer. You can use this library to communicate with the Quark Relayer contract, e.g. to get your Quark wallet address:

import * as Quark from '@compound-finance/quark';

// Get the relayer (an Ethers contract)
let relayer = await Quark.getRelayer(provider);

// Note: Relayer function names end in nonce numbers to help differentiate function calls from transaction scripts

// quarkAddress25(address) returns your Quark wallet address
console.log(await relayer.quarkAddress25('0x...'))

You can also pass options when getting the relayer, which makes this synchronous e.g.

import * as Quark from '@compound-finance/quark';

// Get the relayer version 1 on arbitrum (note: only version 1 exists)
let relayer = Quark.Relayer(provider, 'arbitrum', 1);

Quark Scripts

The easiest way to get running is to simply run Quark Scripts, which are compiled Solidity scripts that ship with Quark. For instance, you can use send to wrap a standard Ethereum call. You can access these contracts directly, but usually you'll use a helper function for common actions.

Scripts come precompiled and do not require solc to run.

import * as Quark from '@compound-finance/quark';

let usdc = new ethers.Contract("0x...", [
  "function balanceOf(address owner) view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)",
  "function transfer(address to, uint amount) returns (bool)",
  "function transferFrom(address spender, address recipient, uint amount) returns (bool)",
], provider);

let tx = await Quark.Scripts.send(provider, usdc, 'transfer', ['0x...', 100e6]);

You can also run multiple calls and you can use populateTransaction to pass in calls, e.g.

import * as Quark from '@compound-finance/quark';

let usdc = new ethers.Contract("0x...", [
  "function balanceOf(address owner) view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)",
  "function transfer(address to, uint amount) returns (bool)",
  "function transferFrom(address spender, address recipient, uint amount) returns (bool)",
], provider);

let tx = await Quark.Scripts.send(provider, [
  usdc.populateTransaction.transfer('0x...', 100e6),
  [usdc, 'transferFrom', ['0x...', 50e6],
]);

TODO: Think about searcher script TODO: Think about other good scripts

Quark Commands

Running Ethers function

While an Ethereum transaction is usually just data sent to a smart contract, Quark transactions are transaction scripts, that is: they run EVM code. This allows Quark scripts to be extremely powerful, but sometimes you want to just do the standard thing (send a simple function call to an existing smart contract). Quark lets you easily do that by wrapping the Ethers call in a simple transaction script:

import * as Quark from '@compound-finance/quark';
import * as solc from 'solc';

let usdc = new ethers.Contract("0x...", [
  "function balanceOf(address owner) view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)",
  "function transfer(address to, uint amount) returns (bool)",
], provider);

let command = await Quark.wrap(
  usdc.populateTransaction.approve(...), solc.compile);

console.log(`Command: ${command.description}`);
console.log(`Command YUL: ${command.yul}`);
console.log(`Command Bytecode: ${command.bytecode}`);

let tx = await Quark.exec(provider, command);

If you have raw data, you can easily send that, as well, or pass in a custom relayer, e.g.:

import * as Quark from '@compound-finance/quark';
import * as solc from 'solc';

let command = await Quark.wrap(
  { to: '0x...', data: '0x...' }, solc.compile);

console.log(`Command: ${command.description}`);
console.log(`Command YUL: ${command.yul}`);
console.log(`Command Bytecode: ${command.bytecode}`);

let tx = await Quark.exec(relayer, command);

Pipelines and Built-ins

Quark becomes more interesting when you start to pipeline actions which will run atomically. While there's no limit to what Quark scripts can do, this library provides a simple way to pipeline actions, including passing data from one step in the pipeline to the next. Pipeline steps can be wrapped Ethers calls, or native Quark "built-ins" that provide more fine-grained access to DeFi functions.

For example, here's a simple pipeline to approve and supply using native built-ins. Notice that the approval amount and the supply amount are based on reading the exact Erc20 balance of the token.

import * as Quark from '@compound-finance/quark';
import * as Erc20 from '@compound-finance/quark/builtins/erc20/arbitrum';
import * as cUSDCv3 from '@compound-finance/quark/builtins/comet/arbitrum';
import * as solc from 'solc';

let action = Quark.pipeline([
  Erc20.approve(cUSDCv3.underlying, cUSDCv3.address, Erc20.balanceOf(cUSDCv3.underlying, cUSDCv3.address)),
  cUSDCv3.supply(cUSDCv3.underlying, Erc20.balanceOf(cUSDCv3.underlying, cUSDCv3.address)),
]);

let command = await Quark.prepare(action, solc.compile);

console.log(`Command: ${command.description}`);
console.log(`Command YUL: ${command.yul}`);
console.log(`Command Bytecode: ${command.bytecode}`);

let tx = await Quark.exec(provider, command);

Note: you can also pipe using the pipe command explicitly to prevent double-reading the balance, e.g.:

import * as Quark from '@compound-finance/quark';
import * as Erc20 from '@compound-finance/quark/builtins/erc20/arbitrum';
import * as cUSDCv3 from '@compound-finance/quark/builtins/comet/arbitrum';
import * as solc from 'solc';

let action = Quark.pipeline([
  pipe(Erc20.balanceOf(cUSDCv3.underlying, cUSDCv3.address), (bal) => [
    Erc20.approve(cUSDCv3.underlying, cUSDCv3.address, bal),
    cUSDCv3.supply(cUSDCv3.underlying, bal),
  ])
]);

let command = await Quark.prepare(action, solc.compile);

console.log(`Command: ${command.description}`);
console.log(`Command YUL: ${command.yul}`);
console.log(`Command Bytecode: ${command.bytecode}`);

let tx = await Quark.exec(provider, command);

You can also perform more complex actions, like combining Uniswap and Compound, e.g.

import * as Quark from '@compound-finance/quark';
import * as Erc20 from '@compound-finance/quark/builtins/tokens';
import * as cUSDCv3 from '@compound-finance/quark/builtins/comet/arbitrum';
import * as Uniswap from '@compound-finance/quark/builtins/uniswap/arbitrum';

let action = Quark.pipeline([
  Quark.pipe(Uniswap.singleSwap(cUSDCv3.underlying, Erc20.arbitrum.uni, new Quark.Uint256(1e18)), (swapAmount) => [
    Erc20.approve(Erc20.arbitrum.uni, cUSDCv3.address, swapAmount),
    cUSDCv3.supply(cUSDCv3.underlying, swapAmount),
  ]),
]);

You can also wrap Ethers calls as a pipeline action via invoke. Note: you cannot easily pipe values from Ethers calls to other Ethers calls. Thus, you should prefer to use builtins where possible as they compose better.

import * as Quark from '@compound-finance/quark';
import { invoke, readUint256 } from '@compound-finance/quark';
import * as cUSDCv3 from '@compound-finance/quark/builtins/comet/arbitrum';

let usdc = new ethers.Contract("0x...", [
  "function balanceOf(address owner) view returns (uint256)",
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)",
  "function transfer(address to, uint amount) returns (bool)",
  "function approve(address spender, uint amount) returns (bool)",
], provider);

let comet = new ethers.Contract("0x...", [
  "function supply(address asset, uint256 amount)"
], provider);

let action = pipeline([
  invoke(await usdc.populateTransaction.approve(cUSDCv3.address.get(), Quark.UINT256_MAX.get())),
  pipe(readUint256(usdc.balanceOf(cUSDCv3.address.get())), (bal) => [ // Read from Ethers call
    cUSDCv3.supply(cUSDCv3.underlying, bal) // Can pipe only to built-ins, not to Ethers calls
  ])
]);

Solidity

** Solidity Support is Experimental

There is also experimental support for running Solidity code directly as a Quark command.

import * as Quark from '@compound-finance/quark';
import * as solc from 'solc';

let command = await Quark.buildSol(`
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Fun {
  event FunTimes(uint256);

  function hello() external {
    emit FunTimes(55);
  }
}
`, solc.compile); // Pass solc compilation command

console.log(`Command: ${command.description}`);
console.log(`Command YUL: ${command.yul}`);
console.log(`Command Bytecode: ${command.bytecode}`);

let tx = await Quark.exec(provider, command);

There are a lot of potential limitations in these scripts as we have less control over the generated code. It's recommended you inspect the Yul code directly to make sure it does what you would expect.

Yul

You can also build a command directly from Yul.

import * as Quark from '@compound-finance/quark';
import * as solc from 'solc';

let command = await Quark.buildYul(`
object "Ping" {
  code {
    // Store a value (55) in memory
    mstore(0x80, 55)

    // ABI topic for \`Ping(uint256)\`
    let topic := 0x48257dc961b6f792c2b78a080dacfed693b660960a702de21cee364e20270e2f

    // emit Ping(55)
    log1(0x80, 0x20, topic)

    return(0, 0)
  }
}
`, solc.compile); // Pass solc compilation command

console.log(`Command: ${command.description}`);
console.log(`Command YUL: ${command.yul}`);
console.log(`Command Bytecode: ${command.bytecode}`);

let tx = await Quark.exec(provider, command);

Future Considerations

We could probably improve the ability to pipe data into Ethers invocations, but that is starting to get a little dicey. Leaving it as a note for now to address later.

We should make Solc/Yul compilation (optionally?) outside of the main thread.

License

Copyright Geoffrey Hayes, Compound Labs, Inc. 2023. All rights reserved.

This software is provided "as-is" with no warranty whatsoever. By using this software, you agree that you shall arise no claim against the author or their representatives from any usage of this software.