Skip to content
Merged

0x v2 #739

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 272 additions & 0 deletions packages/relayer/src/precondition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
import { ethers } from 'ethers'

import { proto } from './rpc-relayer'

export type Precondition =
| NativeBalancePrecondition
| Erc20BalancePrecondition
| Erc20ApprovalPrecondition
| Erc721OwnershipPrecondition
| Erc721ApprovalPrecondition
| Erc1155BalancePrecondition
| Erc1155ApprovalPrecondition

export function isPrecondition(precondition: any): precondition is Precondition {
return [
isNativeBalancePrecondition,
isErc20BalancePrecondition,
isErc20ApprovalPrecondition,
isErc721OwnershipPrecondition,
isErc721ApprovalPrecondition,
isErc1155BalancePrecondition,
isErc1155ApprovalPrecondition
].some(predicate => predicate(precondition))
}

export function encodePrecondition(precondition: Precondition): proto.Precondition {
if (isNativeBalancePrecondition(precondition)) {
return encodeNativeBalancePrecondition(precondition)
} else if (isErc20BalancePrecondition(precondition)) {
return encodeErc20BalancePrecondition(precondition)
} else if (isErc20ApprovalPrecondition(precondition)) {
return encodeErc20ApprovalPrecondition(precondition)
} else if (isErc721OwnershipPrecondition(precondition)) {
return encodeErc721OwnershipPrecondition(precondition)
} else if (isErc721ApprovalPrecondition(precondition)) {
return encodeErc721ApprovalPrecondition(precondition)
} else if (isErc1155BalancePrecondition(precondition)) {
return encodeErc1155BalancePrecondition(precondition)
} else if (isErc1155ApprovalPrecondition(precondition)) {
return encodeErc1155ApprovalPrecondition(precondition)
} else {
throw new Error('unreachable')
}
}

type NativeBalancePrecondition = {
type: 'native-balance'
address: `0x${string}`
min?: ethers.BigNumberish
max?: ethers.BigNumberish
}

function isNativeBalancePrecondition(precondition: any): precondition is NativeBalancePrecondition {
return (
typeof precondition === 'object' &&
precondition &&
precondition.type === 'native-balance' &&
ethers.isAddress(precondition.address) &&
(precondition.min === undefined || isBigNumberish(precondition.min)) &&
(precondition.max === undefined || isBigNumberish(precondition.max))
)
}

function encodeNativeBalancePrecondition(precondition: NativeBalancePrecondition): proto.Precondition {
return {
type: precondition.type,
precondition: {
...precondition,
Comment thread
Dargon789 marked this conversation as resolved.
type: undefined,
min: encodeBigNumberish(precondition.min),
max: encodeBigNumberish(precondition.max)
}
}
}

type Erc20BalancePrecondition = {
type: 'erc20-balance'
address: `0x${string}`
token: `0x${string}`
min?: ethers.BigNumberish
max?: ethers.BigNumberish
}

function isErc20BalancePrecondition(precondition: any): precondition is Erc20BalancePrecondition {
return (
typeof precondition === 'object' &&
precondition &&
precondition.type === 'erc20-balance' &&
ethers.isAddress(precondition.address) &&
ethers.isAddress(precondition.token) &&
(precondition.min === undefined || isBigNumberish(precondition.min)) &&
(precondition.max === undefined || isBigNumberish(precondition.max))
)
}

function encodeErc20BalancePrecondition(precondition: Erc20BalancePrecondition): proto.Precondition {
return {
type: precondition.type,
precondition: {
...precondition,
type: undefined,
min: encodeBigNumberish(precondition.min),
max: encodeBigNumberish(precondition.max)
}
}
}

type Erc20ApprovalPrecondition = {
type: 'erc20-approval'
address: `0x${string}`
token: `0x${string}`
operator: `0x${string}`
min: ethers.BigNumberish
}

function isErc20ApprovalPrecondition(precondition: any): precondition is Erc20ApprovalPrecondition {
return (
typeof precondition === 'object' &&
precondition &&
precondition.type === 'erc20-approval' &&
ethers.isAddress(precondition.address) &&
ethers.isAddress(precondition.token) &&
ethers.isAddress(precondition.operator) &&
isBigNumberish(precondition.min)
)
}

function encodeErc20ApprovalPrecondition(precondition: Erc20ApprovalPrecondition): proto.Precondition {
return {
type: precondition.type,
precondition: { ...precondition, type: undefined, min: encodeBigNumberish(precondition.min) }
}
}

type Erc721OwnershipPrecondition = {
type: 'erc721-ownership'
address: `0x${string}`
token: `0x${string}`
tokenId: ethers.BigNumberish
owned?: boolean
}

function isErc721OwnershipPrecondition(precondition: any): precondition is Erc721OwnershipPrecondition {
return (
typeof precondition === 'object' &&
precondition.type === 'erc721-ownership' &&
ethers.isAddress(precondition.address) &&
ethers.isAddress(precondition.token) &&
isBigNumberish(precondition.tokenId) &&
(precondition.owned === undefined || typeof precondition.owned === 'boolean')
Comment thread
Dargon789 marked this conversation as resolved.
)
}

function encodeErc721OwnershipPrecondition(precondition: Erc721OwnershipPrecondition): proto.Precondition {
return {
type: precondition.type,
precondition: {
...precondition,
type: undefined,
tokenId: encodeBigNumberish(precondition.tokenId),
owned: precondition.owned !== false
}
}
}

type Erc721ApprovalPrecondition = {
type: 'erc721-approval'
address: `0x${string}`
token: `0x${string}`
tokenId: ethers.BigNumberish
operator: `0x${string}`
}

function isErc721ApprovalPrecondition(precondition: any): precondition is Erc721ApprovalPrecondition {
return (
typeof precondition === 'object' &&
precondition.type === 'erc721-approval' &&
ethers.isAddress(precondition.address) &&
ethers.isAddress(precondition.token) &&
isBigNumberish(precondition.tokenId) &&
ethers.isAddress(precondition.operator)
)
}

function encodeErc721ApprovalPrecondition(precondition: Erc721ApprovalPrecondition): proto.Precondition {
return {
type: precondition.type,
precondition: { ...precondition, type: undefined, tokenId: encodeBigNumberish(precondition.tokenId) }
}
}

type Erc1155BalancePrecondition = {
type: 'erc1155-balance'
address: `0x${string}`
token: `0x${string}`
tokenId: ethers.BigNumberish
min?: ethers.BigNumberish
max?: ethers.BigNumberish
}

function isErc1155BalancePrecondition(precondition: any): precondition is Erc1155BalancePrecondition {
return (
typeof precondition === 'object' &&
precondition &&
precondition.type === 'erc1155-balance' &&
ethers.isAddress(precondition.address) &&
ethers.isAddress(precondition.token) &&
isBigNumberish(precondition.tokenId) &&
(precondition.min === undefined || isBigNumberish(precondition.min)) &&
(precondition.max === undefined || isBigNumberish(precondition.max))
)
}

function encodeErc1155BalancePrecondition(precondition: Erc1155BalancePrecondition): proto.Precondition {
return {
type: precondition.type,
precondition: {
...precondition,
type: undefined,
tokenId: encodeBigNumberish(precondition.tokenId),
min: encodeBigNumberish(precondition.min),
max: encodeBigNumberish(precondition.max)
}
}
}

type Erc1155ApprovalPrecondition = {
type: 'erc1155-approval'
address: `0x${string}`
token: `0x${string}`
tokenId: ethers.BigNumberish
operator: `0x${string}`
min: ethers.BigNumberish
}

function isErc1155ApprovalPrecondition(precondition: any): precondition is Erc1155ApprovalPrecondition {
return (
typeof precondition === 'object' &&
precondition &&
precondition.type === 'erc1155-approval' &&
ethers.isAddress(precondition.address) &&
ethers.isAddress(precondition.token) &&
isBigNumberish(precondition.tokenId) &&
ethers.isAddress(precondition.operator) &&
isBigNumberish(precondition.min)
)
}

function encodeErc1155ApprovalPrecondition(precondition: Erc1155ApprovalPrecondition): proto.Precondition {
return {
type: precondition.type,
precondition: {
...precondition,
type: undefined,
tokenId: encodeBigNumberish(precondition.tokenId),
min: encodeBigNumberish(precondition.min)
}
}
}

function isBigNumberish(value: any): value is ethers.BigNumberish {
try {
ethers.toBigInt(value)
return true
} catch {
return false
}
}

function encodeBigNumberish(value: ethers.BigNumberish | undefined): string | undefined {
return value !== undefined ? ethers.toBigInt(value).toString() : undefined
}
46 changes: 41 additions & 5 deletions packages/services/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
export * from './api.gen.js'
import {
Precondition as ChainPrecondition,
encodePrecondition as encodeChainPrecondition,
isPrecondition as isChainPrecondition
} from '@0xsequence/relayer'
import { ethers } from 'ethers'

import { API as ApiRpc } from './api.gen.js'
import * as proto from './api.gen'

export class SequenceAPIClient extends ApiRpc {
export class SequenceAPIClient extends proto.API {
constructor(
hostname: string,
public projectAccessKey?: string,
public jwtAuth?: string,
public jwtAuth?: string
) {
super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch)
this.fetch = this._fetch
Expand All @@ -15,7 +20,7 @@ export class SequenceAPIClient extends ApiRpc {
_fetch = (input: RequestInfo, init?: RequestInit): Promise<Response> => {
// automatically include jwt and access key auth header to requests
// if its been set on the api client
const headers: Record<string, string> = {}
const headers: { [key: string]: any } = {}
Comment thread
Dargon789 marked this conversation as resolved.

const jwtAuth = this.jwtAuth
const projectAccessKey = this.projectAccessKey
Expand All @@ -34,3 +39,34 @@ export class SequenceAPIClient extends ApiRpc {
return fetch(input, init)
}
}

export * from './api.gen'

export type Precondition = { chainId: ethers.BigNumberish } & ChainPrecondition

export function isPrecondition(precondition: any): precondition is Precondition {
return (
typeof precondition === 'object' && precondition && isBigNumberish(precondition.chainId) && isChainPrecondition(precondition)
)
}

export function encodePrecondition(precondition: Precondition): proto.Precondition {
const { type, precondition: args } = encodeChainPrecondition(precondition)
delete args.chainId
Comment thread
Dargon789 marked this conversation as resolved.
return { type, chainID: encodeBigNumberish(precondition.chainId), precondition: args }
}

function isBigNumberish(value: any): value is ethers.BigNumberish {
try {
ethers.toBigInt(value)
return true
} catch {
return false
}
}

function encodeBigNumberish<T extends ethers.BigNumberish | undefined>(
value: T
): T extends ethers.BigNumberish ? string : undefined {
return value !== undefined ? ethers.toBigInt(value).toString() : (undefined as any)
Comment thread
Dargon789 marked this conversation as resolved.
}
43 changes: 43 additions & 0 deletions packages/services/builder/src/Example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { sequence } from '@0xsequence/provider'
import { ethers } from 'ethers'
import { SequenceBatchBuilder } from './services/sequence/SequenceBatchBuilder'

async function runExample() {
const wallet = await sequence.initWallet('mainnet')
const signer = wallet.getSigner(56) // BSC Chain ID: 56

const busdAddress = '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56'
const dexRouter = '0x1111111254fb6c44bac0bed2854e76f90643097d'
const recipient = '0x9a72807e1BC8A5e1E178f51E26239d58F511EB3D'

// Initialize Helper
const batchBuilder = new SequenceBatchBuilder(signer)

// batchBuilder (Chainable)
batchBuilder
.addERC20Approve({
tokenAddress: busdAddress,
spender: dexRouter,
amount: ethers.utils.parseUnits('100.0', 18)
})
.addERC20Transfer({
tokenAddress: busdAddress,
recipient: recipient,
amount: ethers.utils.parseUnits('50.0', 18)
})

// (Optional) pull Payload out Simulate on Tenderly
const payloadForSimulation = batchBuilder.getBatchQueue()
console.log('Payload for Simulation:', JSON.stringify(payloadForSimulation, null, 2))

// 3. sent Execute
try {
const tx = await batchBuilder.execute()
console.log('Batch Transaction Submitted! Tx Hash:', tx.hash)

const receipt = await tx.wait()
console.log('✅ Success in Block:', receipt.blockNumber)
} catch (error) {
console.error('❌ Batch Execution Failed:', error)
}
}
Loading