@sfpro/sdk
Version:
TypeScript SDK for Superfluid Protocol - stream money every second
400 lines (350 loc) • 11.9 kB
text/typescript
import { createWalletClient, encodeAbiParameters, encodeFunctionData, parseAbiParameters, publicActions } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { optimismSepolia } from "viem/chains"
import { describe, test } from "vitest"
import { superTokenAbi } from "../src/abi"
import {
legacyVestingSchedulerV2Abi,
legacyVestingSchedulerV2Address,
vestingSchedulerV3Abi,
vestingSchedulerV3Address,
} from "../src/abi/automation"
import { cfaAbi, cfaAddress, hostAbi, hostAddress } from "../src/abi/core"
import { superfluidTestnetTransports } from "../src/config"
import { OPERATION_TYPE, type Operation, prepareOperation, stripFunctionSelector } from "../src/constant"
describe("Superfluid batch call tests", () => {
test("should be able to simulate a transaction with all the operation types (raw)", async () => {
const privateKey = process.env.PRIVATE_KEY
if (!privateKey) {
throw new Error("PRIVATE_KEY environment variable is not set.")
}
// Ensure the private key is a valid hex string
const account = privateKeyToAccount(privateKey as `0x${string}`)
console.log(`Connected to account: ${account.address}`)
const chain = optimismSepolia
const client = createWalletClient({
account,
chain,
transport: superfluidTestnetTransports[chain.id],
}).extend(publicActions)
console.log(`Connected to chain: ${chain.name}`)
const wrapperSuperTokenAddress = "0x131780640edf9830099aac2203229073d6d2fe69" as const // USDCx
const vitalikAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" as const
// # ERC20_APPROVE
const erc20Approve = encodeFunctionData({
abi: superTokenAbi,
functionName: "approve",
args: [vitalikAddress, 1n],
})
const erc20ApproveOperation: Operation = {
operationType: OPERATION_TYPE.ERC20_APPROVE,
target: wrapperSuperTokenAddress,
data: stripFunctionSelector(erc20Approve),
}
// ---
// # ERC20_TRANSFER_FROM
const erc20TransferFrom = encodeFunctionData({
abi: superTokenAbi,
functionName: "transferFrom",
args: [account.address, vitalikAddress, 1n],
})
const erc20TransferFromOperation: Operation = {
operationType: OPERATION_TYPE.ERC20_TRANSFER_FROM,
target: wrapperSuperTokenAddress,
data: stripFunctionSelector(erc20TransferFrom),
}
// ---
// # ERC777_SEND
const erc777Send = encodeFunctionData({
abi: superTokenAbi,
functionName: "send",
args: [vitalikAddress, 1n, "0x"],
})
const erc777SendOperation: Operation = {
operationType: OPERATION_TYPE.ERC777_SEND,
target: wrapperSuperTokenAddress,
data: stripFunctionSelector(erc777Send),
}
// ---
// # ERC20_INCREASE_ALLOWANCE
const erc20IncreaseAllowance = encodeFunctionData({
abi: superTokenAbi,
functionName: "increaseAllowance",
args: [vitalikAddress, 1n],
})
const erc20IncreaseAllowanceOperation: Operation = {
operationType: OPERATION_TYPE.ERC20_INCREASE_ALLOWANCE,
target: wrapperSuperTokenAddress,
data: stripFunctionSelector(erc20IncreaseAllowance),
}
// ---
// # ERC20_DECREASE_ALLOWANCE
const erc20DecreaseAllowance = encodeFunctionData({
abi: superTokenAbi,
functionName: "decreaseAllowance",
args: [vitalikAddress, 1n],
})
const erc20DecreaseAllowanceOperation: Operation = {
operationType: OPERATION_TYPE.ERC20_DECREASE_ALLOWANCE,
target: wrapperSuperTokenAddress,
data: stripFunctionSelector(erc20DecreaseAllowance),
}
// ---
// # SUPERTOKEN_UPGRADE
const superTokenUpgrade = encodeFunctionData({
abi: superTokenAbi,
functionName: "upgrade",
args: [1n],
})
const superTokenUpgradeOperation: Operation = {
operationType: OPERATION_TYPE.SUPERTOKEN_UPGRADE,
target: wrapperSuperTokenAddress,
data: stripFunctionSelector(superTokenUpgrade),
}
// ---
// # SUPERTOKEN_DOWNGRADE
const superTokenDowngrade = encodeFunctionData({
abi: superTokenAbi,
functionName: "downgrade",
args: [1n],
})
const superTokenDowngradeOperation: Operation = {
operationType: OPERATION_TYPE.SUPERTOKEN_DOWNGRADE,
target: wrapperSuperTokenAddress,
data: stripFunctionSelector(superTokenDowngrade),
}
// ---
// Note: native asset upgrade(ByEth) & downgrade(ToEth) are not supported.
// # SUPERFLUID_CALL_AGREEMENT
const callAgreementCreateFlowInternal = encodeFunctionData({
abi: cfaAbi,
functionName: "createFlow",
args: [wrapperSuperTokenAddress, vitalikAddress, 1n, "0x"],
})
const userData = "0x"
const callAgreementCreateFlow = encodeAbiParameters(parseAbiParameters("bytes, bytes"), [
callAgreementCreateFlowInternal,
userData,
])
const callAgreementCreateFlowOperation: Operation = {
operationType: OPERATION_TYPE.SUPERFLUID_CALL_AGREEMENT,
target: cfaAddress[chain.id],
data: callAgreementCreateFlow,
}
// ---
// # CALL_APP_ACTION
const callAppActionCreateVestingSchedule = encodeFunctionData({
abi: legacyVestingSchedulerV2Abi,
functionName: "createVestingSchedule",
args: [wrapperSuperTokenAddress, vitalikAddress, 1893448800, 0, 1n, 0n, 1924984800, "0x"],
})
const callAppActionCreateVestingScheduleOperation: Operation = {
operationType: OPERATION_TYPE.CALL_APP_ACTION,
target: legacyVestingSchedulerV2Address[chain.id],
data: callAppActionCreateVestingSchedule,
}
// ---
// # SIMPLE_FORWARD_CALL
const simpleForwardCallPayableValue = 1n
const simpleForwardCallOperation: Operation = {
operationType: OPERATION_TYPE.SIMPLE_FORWARD_CALL,
target: vitalikAddress,
data: "0x",
} as const
// ---
// # ERC2771_FORWARD_CALL
const erc2771ForwardCall = encodeFunctionData({
abi: vestingSchedulerV3Abi,
functionName: "createVestingSchedule",
args: [wrapperSuperTokenAddress, vitalikAddress, 1893448800, 0, 1n, 0n, 1924984800],
})
const erc2771ForwardCallOperation: Operation = {
operationType: OPERATION_TYPE.ERC2771_FORWARD_CALL,
target: vestingSchedulerV3Address[chain.id],
data: erc2771ForwardCall,
}
// ---
const batchCall = await client.simulateContract({
address: hostAddress[chain.id],
abi: hostAbi,
functionName: "batchCall",
value: simpleForwardCallPayableValue,
args: [
[
simpleForwardCallOperation, // Put payable operation with value transfer first
erc20ApproveOperation,
erc20TransferFromOperation,
erc777SendOperation,
erc20IncreaseAllowanceOperation,
erc20DecreaseAllowanceOperation,
superTokenUpgradeOperation,
superTokenDowngradeOperation,
callAgreementCreateFlowOperation,
callAppActionCreateVestingScheduleOperation,
erc2771ForwardCallOperation,
],
],
})
console.log(batchCall.request)
})
test("should be able to simulate a transaction with all the operation types (using helper function)", async () => {
const privateKey = process.env.PRIVATE_KEY
if (!privateKey) {
throw new Error("PRIVATE_KEY environment variable is not set.")
}
// Ensure the private key is a valid hex string
const account = privateKeyToAccount(privateKey as `0x${string}`)
console.log(`Connected to account: ${account.address}`)
const chain = optimismSepolia
const client = createWalletClient({
account,
chain,
transport: superfluidTestnetTransports[chain.id],
}).extend(publicActions)
console.log(`Connected to chain: ${chain.name}`)
const wrapperSuperTokenAddress = "0x131780640edf9830099aac2203229073d6d2fe69" as const // USDCx
const vitalikAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" as const
// # ERC20_APPROVE
const erc20ApproveOperation = prepareOperation({
operationType: OPERATION_TYPE.ERC20_APPROVE,
target: wrapperSuperTokenAddress,
data: encodeFunctionData({
abi: superTokenAbi,
functionName: "approve",
args: [vitalikAddress, 1n],
}),
})
// ---
// # ERC20_TRANSFER_FROM
const erc20TransferFromOperation = prepareOperation({
operationType: OPERATION_TYPE.ERC20_TRANSFER_FROM,
target: wrapperSuperTokenAddress,
data: encodeFunctionData({
abi: superTokenAbi,
functionName: "transferFrom",
args: [account.address, vitalikAddress, 1n],
}),
})
// ---
// # ERC777_SEND
const erc777SendOperation = prepareOperation({
operationType: OPERATION_TYPE.ERC777_SEND,
target: wrapperSuperTokenAddress,
data: encodeFunctionData({
abi: superTokenAbi,
functionName: "send",
args: [vitalikAddress, 1n, "0x"],
}),
})
// ---
// # ERC20_INCREASE_ALLOWANCE
const erc20IncreaseAllowanceOperation = prepareOperation({
operationType: OPERATION_TYPE.ERC20_INCREASE_ALLOWANCE,
target: wrapperSuperTokenAddress,
data: encodeFunctionData({
abi: superTokenAbi,
functionName: "increaseAllowance",
args: [vitalikAddress, 1n],
}),
})
// ---
// # ERC20_DECREASE_ALLOWANCE
const erc20DecreaseAllowanceOperation = prepareOperation({
operationType: OPERATION_TYPE.ERC20_DECREASE_ALLOWANCE,
target: wrapperSuperTokenAddress,
data: encodeFunctionData({
abi: superTokenAbi,
functionName: "decreaseAllowance",
args: [vitalikAddress, 1n],
}),
})
// ---
// # SUPERTOKEN_UPGRADE
const superTokenUpgradeOperation = prepareOperation({
operationType: OPERATION_TYPE.SUPERTOKEN_UPGRADE,
target: wrapperSuperTokenAddress,
data: encodeFunctionData({
abi: superTokenAbi,
functionName: "upgrade",
args: [1n],
}),
})
// ---
// # SUPERTOKEN_DOWNGRADE
const superTokenDowngradeOperation = prepareOperation({
operationType: OPERATION_TYPE.SUPERTOKEN_DOWNGRADE,
target: wrapperSuperTokenAddress,
data: encodeFunctionData({
abi: superTokenAbi,
functionName: "downgrade",
args: [1n],
}),
})
// ---
// Note: native asset upgrade(ByEth) & downgrade(ToEth) are not supported.
// # SUPERFLUID_CALL_AGREEMENT
const callAgreementCreateFlowOperation = prepareOperation({
operationType: OPERATION_TYPE.SUPERFLUID_CALL_AGREEMENT,
target: cfaAddress[chain.id],
data: encodeFunctionData({
abi: cfaAbi,
functionName: "createFlow",
args: [wrapperSuperTokenAddress, vitalikAddress, 1n, "0x"],
}),
userData: "0x",
})
// ---
// # CALL_APP_ACTION
const callAppActionCreateVestingScheduleOperation = prepareOperation({
operationType: OPERATION_TYPE.CALL_APP_ACTION,
target: legacyVestingSchedulerV2Address[chain.id],
data: encodeFunctionData({
abi: legacyVestingSchedulerV2Abi,
functionName: "createVestingSchedule",
args: [wrapperSuperTokenAddress, vitalikAddress, 1893448800, 0, 1n, 0n, 1924984800, "0x"],
}),
})
// ---
// # SIMPLE_FORWARD_CALL
const simpleForwardCallPayableValue = 1n
const simpleForwardCallOperation = prepareOperation({
operationType: OPERATION_TYPE.SIMPLE_FORWARD_CALL,
target: vitalikAddress,
data: "0x",
})
// ---
// # ERC2771_FORWARD_CALL
const erc2771ForwardCallOperation = prepareOperation({
operationType: OPERATION_TYPE.ERC2771_FORWARD_CALL,
target: vestingSchedulerV3Address[chain.id],
data: encodeFunctionData({
abi: vestingSchedulerV3Abi,
functionName: "createVestingSchedule",
args: [wrapperSuperTokenAddress, vitalikAddress, 1893448800, 0, 1n, 0n, 1924984800],
}),
})
// ---
const batchCall = await client.simulateContract({
address: hostAddress[chain.id],
abi: hostAbi,
functionName: "batchCall",
value: simpleForwardCallPayableValue,
args: [
[
simpleForwardCallOperation, // Put payable operation with value transfer first
erc20ApproveOperation,
erc20TransferFromOperation,
erc777SendOperation,
erc20IncreaseAllowanceOperation,
erc20DecreaseAllowanceOperation,
superTokenUpgradeOperation,
superTokenDowngradeOperation,
callAgreementCreateFlowOperation,
callAppActionCreateVestingScheduleOperation,
erc2771ForwardCallOperation,
],
],
})
console.log(batchCall.request)
})
})