@brinkninja/sdk
v5.0.0-beta.61
Published
This module can be used to interact with Brink proxy accounts, either as the account owner/signer or as an executor of messages signed by the account owner/signer.
Downloads
195
Keywords
Readme
Brink SDK
This module can be used to interact with Brink proxy accounts, either as the account owner/signer or as an executor of messages signed by the account owner/signer.
Installation
npm install @brinkninja/sdk
Strategies
Creating Strategies
Strategies can be built from lower-level primitives:
const { Strategy, Order, UniV3TWAP, useBit, marketSwapExactInput } = brink
// configure a TWAP USDC-ETH oracle
const oracle_USDC_ETH = UniV3TWAP({
baseToken: USDC_address,
quoteToken: WETH_address,
time: 1000,
feePool: 500
})
// price ~1000 ETH/USDC
const p = 0.001;
// construct a "stop-loss" strategy
const strategy = Strategy.create(
Order.create(
useBit({ // replay bit
bitmapIndex: 0,
bit: 1
}),
requireBlockNotMined({ // expiry
blockNumber: 16802111
}),
requireUint256LowerBound({ // price oracle check
oracle: oracle_USDC_ETH,
price: p
}),
marketSwapExactInput({ // market swap
oracle: oracle_USDC_ETH,
owner: ownerAddress,
tokenIn: USDC_address,
tokenOut: WETH_address,
tokenInAmount: 3000_000000, // 3k USDC
feePercent: 1_0000, // 1% fee
feeMin: 0
})
)
)
Higher level types are provided for specific recipes
const { StopLoss, Oracles } = brink
const stopLoss = StopLoss.create({
bitmapIndex: 0,
bit: 1,
expiryBlock: 16802111,
oracle: oracle_USDC_ETH,
price: p,
owner: ownerAddress,
tokenIn: USDC_address,
tokenOut: WETH_address,
tokenInAmount: 3000_000000, // 3k USDC
feePercent: 1_0000, // 1% fee
feeMin: 0
})
Strategy Objects
Oracles
UniV3TWAP({
baseToken,
quoteToken,
time,
feePool
})
ReservoirFloorPrice({
priceKind,
twapSeconds,
contractAddress,
timestamp
})
Oracles implement an IOracle interface
IOracle {
contractAddress(); // returns the oracle contract address
params(); // returns bytes for oracle primitive params calls
value(); // reads and returns the value of the oracle on-chain
}
Strategy
Strategy {
create(Order1, ... OrderN); // builds and returns a StrategyInstance
}
StrategyInstance {
orders(); // returns orders
beforeCalls();
afterCalls();
bytes(); // returns encoded strategy bytes for signing
}
Order
Base order type
Order {
create(Primitive1, ... PrimitiveN); // builds and returns an OrderInstance
}
OrderInstance {
primitives(); // returns primitives
bytes(); // returns encoded bytes for the order
}
We can also support more specific order types for convenience
LimitSwap {}
MarketSwap {}
StopLossMarketSwap {}
StopLossLimitSwap {}
...
Primitives
These are mapped from Primitives01.sol
requireBitNotUsed()
requireBitUsed()
useBit()
requireBlockMined()
requireBlockNotMined()
requireUint256LowerBound()
requireUint256UpperBound()
transfer()
marketSwapExactInput()
marketSwapExactOutput()
limitSwap()
...
All return a base primitive instance
PrimitiveInstance {
params(); // returns params
bytes(); // returns encoded bytes for the primitive calldata
}
Recipes
Recipes implement IRecipe interface
IRecipe {
create(<params>); // create the recipe
params(); // return the recipe's params
strategy<StrategyInstance>; // the wrapped strategy
}
Recipe types
StopLoss {
params: {
bitmapIndex,
bit,
expiryBlock,
oracle,
price,
owner,
tokenIn,
tokenOut,
tokenInAmount,
feePercent,
feeMin
}
}
Bracket {
params: {
bitmapIndex,
bit,
expiryBlock,
oracle,
stopLossPrice,
takeProfitPrice,
owner,
tokenIn,
tokenOut,
tokenInAmount,
feePercent,
feeMin
}
}
...
Account
Setup
Create an account instance with the address of the account owner, and an ethers.js provider and signer. The ethers.js provider and signer will be used for sending transactions to the account.
const brink = require('@brinkninja/sdk')
const { Account } = brink({ provider, signer })
const account = Account(ownerAddress)
Read-only Methods
These methods are available to read from account contract state
isDeployed()
Returns true if the account has been deployed
const deployed = await account.isDeployed()
bitUsed(bitmapIndex, bit)
Returns true if the given bitmapIndex
and bit
have been used for a previous limit swap or cancel transaction
Transaction Methods
These methods issue transactions to an account contract. They wrap ethers.js Contract Meta-class methods and can be used in the same way.
The Account instance exposes the ethers.js write method analysis properties estimateGas
, populateTransaction
, and callStatic
. These can be used for any Account transaction. For example, getting transaction data without submitting the transaction can be done like this:
const txData = await account.populateTransaction.ApprovalSwapsV1.tokenToToken(swapSignedMsg, recipientAddress, toAddress, callData)
All of these transactions (except for deploy()
) will include the desired action after account deployment, if the account has not been deployed yet, using DeployAndExecute.sol. If the account is already deployed, the action will be executed directly on the account contract.
deploy()
Deploys the account contract. Throws an error if the contract is already deployed
Example:
const tx = await account.deploy()
externalCall(value, to, data)
Calls externalCall on the account contract.
This can only be called if the ethers.js signer is the owner of the account
Example:
const tx = await account.externalCall(value, to, data)
delegateCall(to, data)
Calls delegateCall on the account contract.
This can only be called if the ethers.js signer is the owner of the account
Example:
const tx = await account.delegateCall(to, data)
metaDelegateCall(signedMessage, unsignedDataArray)
Calls metaDelegateCall on the account contract.
This can only be called with a valid message signed by the owner of the account
Example:
const tx = await account.metaDelegateCall(signedMessage, [unsignedTo, unsignedData])
Verifier metaDelegateCall methods
All contract methods in https://github.com/brinktrade/brink-verifiers/tree/177ee40291d92f3b0da371ea1939e11964b0de18/contracts/Verifiers contracts can be executed through metaDelegateCall
using the SDK.
For example, a ApprovalSwapsV1.tokenToToken()
verifier signed message can be executed by calling account.ApprovalSwapsV1.tokenToToken()
:
await account.ApprovalSwapsV1.tokenToToken(ethToTokenSignedMessage, unsignedRecipient, unsignedTo, unsignedData)
Each verifier function takes a signed message object from AccountSigner as the first param, followed by any unsigned params required by the verifier function.
Supported functions are:
account.CancelVerifier.cancel()
account.TransferVerifier.tokenTransfer()
account.TransferVerifier.ethTransfer()
account.NftTransferVerifier.nftTransfer()
account.ApprovalSwapsV1.tokenToToken()
account.ApprovalSwapsV1.tokenToNft()
account.ApprovalSwapsV1.nftToToken()
account.ApprovalSwapsV1.tokenToERC1155()
account.ApprovalSwapsV1.ERC1155ToToken()
account.ApprovalSwapsV1.ERC1155ToERC1155()
AccountSigner
Handles signing of account messages. These authorize actions that can be taken on the account by executors.
Messages are returned in this format:
{
message: '<signed message hash>',
EIP712TypedData: <object with decoded EIP712 typed data>,
signature: '<the signature>',
signer: '<address of the signer>',
accountAddress: '<address of the account>',
functionName: '<function on Account.sol that is authorized by this message>',
signedParams: <array of signed parameters>
}
Setup
Create an instance of accountSigner with an ethers.js signer and network. The ethers.js signer will be used for all account message signing
const brink = require('@brinkninja/sdk')
const { AccountSigner } = brink({ network })
const accountSigner = AccountSigner(signer, { network })
accountAddress()
Returns the address of the account. This is computed based on the signer's address. Every signer address has a unique account address, which is known before account deployment. Account address is the same on all EVM chains for a given signer.
signerAddress()
Returns the address of the signer
verifier signing functions
Supported functions are:
accountSigner.CancelVerifier.signCancel()
accountSigner.TransferVerifier.signTokenTransfer()
accountSigner.TransferVerifier.signEthTransfer()
accountSigner.NftTransferVerifier.signNftTransfer()
accountSigner.ApprovalSwapsV1.signTokenToToken()
accountSigner.ApprovalSwapsV1.signTokenToNft()
accountSigner.ApprovalSwapsV1.signNftToToken()
accountSigner.ApprovalSwapsV1.signTokenToERC1155()
accountSigner.ApprovalSwapsV1.signERC1155ToToken()
accountSigner.ApprovalSwapsV1.signERC1155ToERC1155()
Supported Verifier Contracts
| Contract | Address | Networks | | --- | --- | --- | | ApprovalSwapsV1.sol | 0x6ef84B098A812B8f4C81cD6d0B0A5a64d17C9B3e |mainnet, goerli| | CancelVerifier.sol | 0xE0670a90E67eda0126D54843267b27Ca6343B2d8 |mainnet, goerli| | TransferVerifier.sol | 0x6df5AE08Ec7aE5CC2E9e3b0850A61AD7C73bC9A9 |mainnet, goerli| | NftTransferVerifier.sol | 0x946CBd55EA50619C599d69Ab230Dff8707987D00 |mainnet, goerli|
Verifier Repos
https://github.com/brinktrade/brink-verifiers https://github.com/brinktrade/brink-verifiers-v2
Custom Verifier Setup
*** WARNING: SIGNING MESSAGES WITH UNSECURE VERIFIER CONTRACTS COULD PUT YOUR FUNDS AT RISK ***
To use custom verifiers, provide an array of verifier definitions:
const { AccountSigner } = brink({
network: 'hardhat',
verifiers: [{
"functionName": "myFunction",
"functionSignature": "myFunction(uint256,uint256)",
"functionSignatureHash": "0x3c447f23",
"contractName": "MyVerifier",
"contractAddress": "0xE100eF1C4339Dd4E4b54d5cBB6CcEfA96071E227",
"paramTypes": [
{
"name": "paramOne",
"type": "uint256",
"signed": true
},
{
"name": "paramTwo",
"type": "uint256",
"signed": false
}
]
}]
})
const signerWithCustomVerifiers = AccountSigner(ethersSigner)