Skip to content

Commit

Permalink
feat: buildInstructions devEx
Browse files Browse the repository at this point in the history
  • Loading branch information
joepegler committed Jan 14, 2025
1 parent ecef056 commit 061709c
Show file tree
Hide file tree
Showing 16 changed files with 355 additions and 171 deletions.
49 changes: 0 additions & 49 deletions src/sdk/account/decorators/buildBalanceInstructions.test.ts

This file was deleted.

52 changes: 0 additions & 52 deletions src/sdk/account/decorators/buildBalanceInstructions.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/sdk/account/decorators/buildBridgeInstructions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
type MultichainSmartAccount,
toMultichainNexusAccount
} from "../toMultiChainNexusAccount"
import { AcrossPlugin } from "../utils/acrossPlugin"
import { toAcrossPlugin } from "../utils/toAcrossPlugin"
import buildBridgeInstructions from "./buildBridgeInstructions"
import { getUnifiedERC20Balance } from "./getUnifiedERC20Balance"

Expand Down Expand Up @@ -41,7 +41,7 @@ describe("mee:buildBridgeInstructions", () => {
const payload = await buildBridgeInstructions({
account: mcNexus,
amount: 1n,
bridgingPlugins: [AcrossPlugin],
bridgingPlugins: [toAcrossPlugin()],
toChain: base,
unifiedBalance
})
Expand Down
10 changes: 5 additions & 5 deletions src/sdk/account/decorators/buildBridgeInstructions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Address, Chain } from "viem"
import type { Instruction } from "../../clients/decorators/mee/getQuote"
import type { BaseMultichainSmartAccount } from "../toMultiChainNexusAccount"
import { AcrossPlugin } from "../utils/acrossPlugin"
import { toAcrossPlugin } from "../utils/toAcrossPlugin"
import type { UnifiedERC20Balance } from "./getUnifiedERC20Balance"
import type { BridgeQueryResult } from "./queryBridge"
import { queryBridge } from "./queryBridge"
Expand Down Expand Up @@ -124,7 +124,7 @@ export type BridgingInstructions = {
* @example
* const instructions = await buildBridgeInstruction(client, {
* amount: BigInt(1000),
* token: mcUSDC,
* mcToken: mcUSDC,
* chain: base
* })
*/
Expand All @@ -137,16 +137,16 @@ export const buildBridgeInstructions = async (
amount: targetAmount,
toChain,
unifiedBalance,
bridgingPlugins = [AcrossPlugin],
bridgingPlugins = [toAcrossPlugin()],
feeData
} = params

// Create token address mapping
const tokenMapping: MultichainAddressMapping = {
on: (chainId: number) =>
unifiedBalance.token.deployments.get(chainId) || "0x",
unifiedBalance.mcToken.deployments.get(chainId) || "0x",
deployments: Array.from(
unifiedBalance.token.deployments.entries(),
unifiedBalance.mcToken.deployments.entries(),
([chainId, address]) => ({
chainId,
address
Expand Down
115 changes: 115 additions & 0 deletions src/sdk/account/decorators/buildInstructions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import type { Address, Chain, LocalAccount } from "viem"
import { base } from "viem/chains"
import { beforeAll, describe, expect, it } from "vitest"
import { toNetwork } from "../../../test/testSetup"
import type { NetworkConfig } from "../../../test/testUtils"
import { type MeeClient, createMeeClient } from "../../clients/createMeeClient"
import {
Instruction,
SupertransactionLike
} from "../../clients/decorators/mee/getQuote"
import { mcUSDC } from "../../constants/tokens"
import {
type MultichainSmartAccount,
toMultichainNexusAccount
} from "../toMultiChainNexusAccount"
import { buildInstructions } from "./buildInstructions"

describe("mee:buildInstructions", () => {
let network: NetworkConfig
let eoaAccount: LocalAccount
let paymentChain: Chain
let paymentToken: Address
let mcNexus: MultichainSmartAccount
let meeClient: MeeClient

beforeAll(async () => {
network = await toNetwork("MAINNET_FROM_ENV_VARS")

paymentChain = network.chain
paymentToken = network.paymentToken!
eoaAccount = network.account!

mcNexus = await toMultichainNexusAccount({
chains: [base, paymentChain],
signer: eoaAccount
})

meeClient = createMeeClient({ account: mcNexus })
})

it("should use the default action while building instructions", async () => {
const instructions = await buildInstructions({
account: mcNexus,
action: {
type: "DEFAULT",
parameters: [
{
calls: [
{
to: "0x0000000000000000000000000000000000000000",
gasLimit: 50000n,
value: 0n
}
],
chainId: 8453
}
]
}
})

expect(instructions).toMatchInlineSnapshot(`
[
{
"calls": [
{
"gasLimit": 50000n,
"to": "0x0000000000000000000000000000000000000000",
"value": 0n,
},
],
"chainId": 8453,
},
]
`)
expect(instructions.length).toBeGreaterThan(0)
})

it("should use the bridge action while building instructions", async () => {
const initialInstructions = await buildInstructions({
account: mcNexus,
action: {
type: "BRIDGE",
parameters: {
amount: BigInt(1000),
mcToken: mcUSDC,
chain: base
}
}
})

const instructions = await buildInstructions({
currentInstructions: initialInstructions,
account: mcNexus,
action: {
type: "DEFAULT",
parameters: [
{
calls: [
{
to: "0x0000000000000000000000000000000000000000",
gasLimit: 50000n,
value: 0n
}
],
chainId: 8453
}
]
}
})

expect(instructions.length).toBe(2)
expect(instructions[0].calls.length).toBe(2) // Bridge instructions generates two calls
expect(instructions[1].calls.length).toBe(1) // Default instruction in this case generates one call
})
})
78 changes: 78 additions & 0 deletions src/sdk/account/decorators/buildInstructions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type { Chain, erc20Abi } from "viem"
import type { Instruction } from "../../clients/decorators/mee/getQuote"
import type { BaseMultichainSmartAccount } from "../toMultiChainNexusAccount"
import type { MultichainContract } from "../utils/getMultichainContract"
import buildBridgeInstructions from "./buildBridgeInstructions"
import { getUnifiedERC20Balance } from "./getUnifiedERC20Balance"

export type BridgeInstructionsForBridgeAction = {
/** The amount of tokens to require */
amount: bigint
/** The token to require */
mcToken: MultichainContract<typeof erc20Abi>
/** The chain to require the token on */
chain: Chain
}

/**
* Parameters for querying bridge operations
*/
export type BuildInstructionsParams = {
/** The multichain smart account to check balances for */
account: BaseMultichainSmartAccount
/** The chain to build instructions for */
action: DefaultBuildAction | BridgeBuildAction
/** The current instructions */
currentInstructions?: Instruction[]
}

type DefaultBuildAction = {
type: "DEFAULT"
parameters: Instruction[] | Instruction
}

type BridgeBuildAction = {
type: "BRIDGE"
parameters: BridgeInstructionsForBridgeAction
}

/**
* Makes sure that the user has enough funds on the selected chain before filling the
* supertransaction. Bridges funds from other chains if needed.
*
* @param client - The Mee client to use
* @param params - The parameters for the balance requirement
* @returns Instructions for any required bridging operations
* @example
* const instructions = await buildInstructions(client, {
* amount: BigInt(1000),
* mcToken: mcUSDC,
* chain: base
* })
*/

export const buildInstructions = async (
params: BuildInstructionsParams
): Promise<Instruction[]> => {
const { account, action, currentInstructions = [] } = params

switch (action.type) {
case "BRIDGE": {
const { amount, mcToken, chain } = action.parameters
const unifiedBalance = await getUnifiedERC20Balance({ mcToken, account })
const { instructions } = await buildBridgeInstructions({
account,
amount: amount,
toChain: chain,
unifiedBalance
})
return [...currentInstructions, ...instructions]
}
default:
return Array.isArray(action.parameters)
? [...currentInstructions, ...action.parameters]
: [...currentInstructions, action.parameters]
}
}

export default buildInstructions
4 changes: 2 additions & 2 deletions src/sdk/account/decorators/getUnifiedERC20Balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type RelevantBalance = UnifiedBalanceItem & { chainId: number }
*/
export type UnifiedERC20Balance = {
/** The multichain ERC20 token contract */
token: MultichainContract<typeof erc20Abi>
mcToken: MultichainContract<typeof erc20Abi>
/** Individual balance breakdown per chain */
breakdown: RelevantBalance[]
} & UnifiedBalanceItem
Expand Down Expand Up @@ -105,6 +105,6 @@ export async function getUnifiedERC20Balance(
}
}),
breakdown: balances,
token: mcToken
mcToken
}
}
8 changes: 4 additions & 4 deletions src/sdk/account/decorators/queryBridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
type MultichainSmartAccount,
toMultichainNexusAccount
} from "../toMultiChainNexusAccount"
import { AcrossPlugin } from "../utils/acrossPlugin"
import { toAcrossPlugin } from "../utils/toAcrossPlugin"
import type { MultichainAddressMapping } from "./buildBridgeInstructions"
import { queryBridge } from "./queryBridge"

Expand Down Expand Up @@ -41,9 +41,9 @@ describe("mee:queryBridge", () => {

const tokenMapping: MultichainAddressMapping = {
on: (chainId: number) =>
unifiedBalance.token.deployments.get(chainId) || "0x",
unifiedBalance.mcToken.deployments.get(chainId) || "0x",
deployments: Array.from(
unifiedBalance.token.deployments.entries(),
unifiedBalance.mcToken.deployments.entries(),
([chainId, address]) => ({ chainId, address })
)
}
Expand All @@ -58,6 +58,6 @@ describe("mee:queryBridge", () => {

expect(payload?.amount).toBeGreaterThan(0n)
expect(payload?.receivedAtDestination).toBeGreaterThan(0n)
expect(payload?.plugin).toBe(AcrossPlugin)
expect(payload?.plugin).toBe(toAcrossPlugin())
})
})
Loading

0 comments on commit 061709c

Please sign in to comment.