Skip to content
Draft
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
4 changes: 4 additions & 0 deletions examples/16-swap-and-split/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
build
types
scripts/.env
3 changes: 3 additions & 0 deletions examples/16-swap-and-split/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"node-option": ["import=tsx/esm"]
}
2 changes: 2 additions & 0 deletions examples/16-swap-and-split/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node-linker=hoisted
legacy-peer-deps=true
108 changes: 108 additions & 0 deletions examples/16-swap-and-split/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import eslintPluginTypeScript from "@typescript-eslint/eslint-plugin"
import eslintParserTypeScript from "@typescript-eslint/parser"
import eslintPluginImport from "eslint-plugin-import"
import eslintPluginSimpleImportSort from "eslint-plugin-simple-import-sort"
import eslintConfigPrettier from "eslint-config-prettier"
import eslintPluginPrettier from "eslint-plugin-prettier"

export default [
{
ignores: ["node_modules/**", "**/dist/**", "**/build/**", "**/.prettierrc.*", "./src/types/**"]
},
{
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
parser: eslintParserTypeScript,
parserOptions: {
project: "./tsconfig.json"
}
},
plugins: {
"@typescript-eslint": eslintPluginTypeScript,
prettier: eslintPluginPrettier,
import: eslintPluginImport,
"simple-import-sort": eslintPluginSimpleImportSort
},
rules: {
...eslintPluginTypeScript.configs.recommended.rules,
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/explicit-function-return-type": "error",
"@typescript-eslint/no-explicit-any": "error",

"prettier/prettier": [
"error",
{
"semi": false,
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "always",
"bracketSpacing": true,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false
}
],

"simple-import-sort/imports": [
"error",
{
groups: [
["^@?\\w"],
["^\\.\\.(?!/?$)", "^\\.\\./?$"],
["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"]
]
}
],
"simple-import-sort/exports": "error",

"comma-spacing": ["error", { before: false, after: true }],
"no-multiple-empty-lines": ["error", { max: 1, maxEOF: 1 }]
},
settings: {
"import/resolver": {
typescript: {
alwaysTryTypes: true,
project: "./tsconfig.json"
}
}
}
},
// configuration for test files
{
files: ["tests/**/*.{ts,tsx}", "**/*.spec.{ts,tsx}", "**/*.test.{ts,tsx}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
parser: eslintParserTypeScript,
parserOptions: {
project: "./tests/tsconfig.json"
}
},
rules: {
"@typescript-eslint/no-unused-expressions": "off"
}
},
{
files: ["scripts/**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
parser: eslintParserTypeScript,
parserOptions: {
project: "./scripts/tsconfig.json"
}
},
settings: {
"import/resolver": {
typescript: {
alwaysTryTypes: true,
project: "./scripts/tsconfig.json"
}
}
}
},
eslintConfigPrettier
]
12 changes: 12 additions & 0 deletions examples/16-swap-and-split/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: 1.0.0
name: Swap And Split Function
description: Swap tokens and transfer the output to multiple recipients
inputs:
- chainId: uint32
- tokenIn: address
- tokenOut: address
- amount: string # e.g., '10.5' = 10.5 USDC
- slippageBps: uint16 # e.g., 50 = 0.50%
- user: address
- allocations: string # e.g., '0xab...c:6075,0xde...f:3925' (60.75% to 0xab...c and 39.25% to 0xde...f)
- maxFeeUsd: string # e.g., '0.01' = 0.01 USD
36 changes: 36 additions & 0 deletions examples/16-swap-and-split/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@mimicprotocol/16-swap-and-split",
"version": "0.0.1",
"license": "Unlicensed",
"private": true,
"type": "module",
"scripts": {
"deploy": "mimic deploy",
"build": "mimic build",
"codegen": "mimic codegen",
"compile": "mimic compile",
"test": "mimic test",
"lint": "eslint .",
"trigger": "tsx scripts/create-trigger.ts",
"prefill-url": "tsx scripts/get-trigger-prefill-url.ts",
"try-function": "tsx scripts/try-function.ts"
},
"devDependencies": {
"@mimicprotocol/cli": "~1.0.0",
"@mimicprotocol/lib-ts": "~0.1.5",
"@mimicprotocol/sdk": "~0.1.3",
"@mimicprotocol/test-ts": "~0.1.0",
"@types/chai": "^5.2.2",
"@types/mocha": "^10.0.10",
"@types/node": "^22.10.5",
"assemblyscript": "0.27.36",
"chai": "^4.3.7",
"dotenv": "^17.2.3",
"eslint": "^9.10.0",
"json-as": "1.1.7",
"mocha": "^10.2.0",
"tsx": "^4.20.3",
"typescript": "^5.8.3",
"visitor-as": "0.11.4"
}
}
52 changes: 52 additions & 0 deletions examples/16-swap-and-split/scripts/create-trigger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Chains, Client, createExecuteOnceTriggerConfig, EthersSigner } from '@mimicprotocol/sdk'
import { config } from 'dotenv'

// Load environment variables from .env file
config({ path: './scripts/.env' })

// TODO: Replace with your deployed function's CID
const FUNCTION_CID = 'YOUR_FUNCTION_CID_HERE'

// TODO: Customize inputs to match your function's input structure
// This template uses the example function's inputs: chainId, token, amount, recipient, maxFee
const inputs = {
chainId: Chains.Optimism,
token: '0x0b2c639c533813f4aa9d7837caf62653d097ff85', // USDC on Optimism
amount: '1',
recipient: '0x...', // TODO: Replace with your recipient address
maxFee: '0.1',
}

async function main(): Promise<void> {
const client = new Client({
signer: EthersSigner.fromPrivateKey(process.env.PRIVATE_KEY!),
})

// Get the manifest for the function
const manifest = await client.functions.getManifest(FUNCTION_CID)

// TODO: Customize the version, description, and other parameters
// - version: Function version (e.g., '1.0.0')
// - description: Human-readable description for this trigger
// - config: When to execute the function (cron schedule, delta, endDate)
// - executionFeeLimit: Maximum fee for execution (use '0' for no limit)
// - minValidations: Minimum number of validations required

await client.triggers.signAndCreate({
functionCid: FUNCTION_CID,
manifest: manifest,
input: inputs,
version: '1.0.0', // TODO: Update to match your function version
description: `Default Transfer - ${inputs.chainId}`,
config: createExecuteOnceTriggerConfig(),
executionFeeLimit: '0',
minValidations: 1,
})

console.log(`Successfully created trigger`)
}

main().catch((error) => {
console.error('Error creating trigger:', error)
process.exit(1)
})
2 changes: 2 additions & 0 deletions examples/16-swap-and-split/scripts/env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Private key for signing triggers
PRIVATE_KEY=your_private_key_here
39 changes: 39 additions & 0 deletions examples/16-swap-and-split/scripts/get-trigger-prefill-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Chains, getTriggerPrefillUrl, TriggerType } from '@mimicprotocol/sdk'

// TODO: Replace with your deployed function's CID
const FUNCTION_CID = 'YOUR_FUNCTION_CID_HERE'

// TODO: Customize inputs to match your function's input structure
const inputs = {
chainId: Chains.Optimism,
token: '0x0b2c639c533813f4aa9d7837caf62653d097ff85', // USDC on Optimism
amount: '1',
recipient: '0x...',
maxFee: '0.1',
}

// TODO: Customize the trigger configuration
const config = {
type: TriggerType.Cron,
schedule: '0 2 * * *', // every day at 2am
delta: '2h',
endDate: Date.now() + 7 * 24 * 60 * 60 * 1000, // one week from now
}

async function main(): Promise<void> {
// TODO: Replace with your parameters
const prefillUrl = getTriggerPrefillUrl({
functionCid: FUNCTION_CID,
input: inputs,
config,
description: 'Example trigger description',
version: '1.0.1',
})

console.log(`Trigger prefill URL: ${prefillUrl}`)
}

main().catch((error) => {
console.error('Error getting trigger prefill URL:', error)
process.exit(1)
})
33 changes: 33 additions & 0 deletions examples/16-swap-and-split/scripts/try-function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Chains, randomEvmAddress } from '@mimicprotocol/sdk'
import { runFunction } from '@mimicprotocol/test-ts'

// TODO: Replace with your function's directory
const FUNCTION_DIR = './build'
const ORACLE_URL = 'https://api-protocol.mimic.fi/oracle'

const chainId = Chains.Optimism

const context = {
user: randomEvmAddress(),
settlers: [{ address: randomEvmAddress(), chainId }],
timestamp: Date.now(),
}

// TODO: Customize inputs to match your function's input structure
const inputs = {
chainId,
token: '0x0b2c639c533813f4aa9d7837caf62653d097ff85', // USDC on Optimism
amount: '1',
recipient: context.user,
maxFee: '0.1',
}

async function main(): Promise<void> {
const result = await runFunction(FUNCTION_DIR, context, { inputs }, ORACLE_URL)
console.log(JSON.stringify(result, null, 2))
}

main().catch((error) => {
console.error('Error running function:', error)
process.exit(1)
})
17 changes: 17 additions & 0 deletions examples/16-swap-and-split/scripts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020"],
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"rootDir": "."
},
"include": ["./**/*.ts"],
"exclude": ["node_modules"]
}
42 changes: 42 additions & 0 deletions examples/16-swap-and-split/src/function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {
Address,
Allocation,
buildSwapAndSplit,
DenominationToken,
ERC20Token,
TokenAmount,
} from '@mimicprotocol/lib-ts'

import { inputs } from './types'

export default function main(): void {
const chainId = inputs.chainId

const tokenIn = ERC20Token.fromAddress(inputs.tokenIn, chainId)
const amountIn = TokenAmount.fromStringDecimal(tokenIn, inputs.amount)

const tokenOut = ERC20Token.fromAddress(inputs.tokenOut, chainId)
const expectedOut = amountIn.toTokenAmount(tokenOut).unwrap()
const minAmountOut = expectedOut.applySlippageBps(inputs.slippageBps)

const allocations: Allocation[] = inputs.allocations.split(',').map<Allocation>((s) => {
const fields = s.split(':')
const recipient = fields[0]
const pct = fields[1]
return new Allocation(Address.fromString(recipient), u16(parseInt(pct)))
})

const maxFee = TokenAmount.fromStringDecimal(DenominationToken.USD(), inputs.maxFeeUsd)

// Creates a swap operation and multiple dynamic call operations to split the output token among multiple recipients.
// Each recipient receives the percentage of the output token specified in the allocations array.
// The last recipient receives its specified percentage plus any remaining balance caused by rounding.
const builder = buildSwapAndSplit(
chainId,
amountIn,
minAmountOut,
allocations,
inputs.user // Optional. If not provided, the context user will be used.
)
builder.addMaxFee(maxFee).build().send()
}
Loading
Loading