@lifi/composer-sdk 0.0.1-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +220 -0
- package/dist/authoring/FlowBuilderCore.cjs +115 -0
- package/dist/authoring/FlowBuilderCore.cjs.map +1 -0
- package/dist/authoring/FlowBuilderCore.d.cts +114 -0
- package/dist/authoring/FlowBuilderCore.d.ts +114 -0
- package/dist/authoring/FlowBuilderCore.js +91 -0
- package/dist/authoring/FlowBuilderCore.js.map +1 -0
- package/dist/authoring/handles.cjs +43 -0
- package/dist/authoring/handles.cjs.map +1 -0
- package/dist/authoring/handles.d.cts +61 -0
- package/dist/authoring/handles.d.ts +61 -0
- package/dist/authoring/handles.js +17 -0
- package/dist/authoring/handles.js.map +1 -0
- package/dist/authoring/raw.cjs +41 -0
- package/dist/authoring/raw.cjs.map +1 -0
- package/dist/authoring/raw.d.cts +47 -0
- package/dist/authoring/raw.d.ts +47 -0
- package/dist/authoring/raw.js +16 -0
- package/dist/authoring/raw.js.map +1 -0
- package/dist/authoring/signatureArgs.cjs +114 -0
- package/dist/authoring/signatureArgs.cjs.map +1 -0
- package/dist/authoring/signatureArgs.d.cts +146 -0
- package/dist/authoring/signatureArgs.d.ts +146 -0
- package/dist/authoring/signatureArgs.js +87 -0
- package/dist/authoring/signatureArgs.js.map +1 -0
- package/dist/client.cjs +140 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +67 -0
- package/dist/client.d.ts +67 -0
- package/dist/client.js +116 -0
- package/dist/client.js.map +1 -0
- package/dist/discovery.cjs +17 -0
- package/dist/discovery.cjs.map +1 -0
- package/dist/discovery.d.cts +56 -0
- package/dist/discovery.d.ts +56 -0
- package/dist/discovery.js +1 -0
- package/dist/discovery.js.map +1 -0
- package/dist/errors.cjs +91 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.cts +77 -0
- package/dist/errors.d.ts +77 -0
- package/dist/errors.js +65 -0
- package/dist/errors.js.map +1 -0
- package/dist/generated/config.generated.cjs +17 -0
- package/dist/generated/config.generated.cjs.map +1 -0
- package/dist/generated/config.generated.d.cts +144 -0
- package/dist/generated/config.generated.d.ts +144 -0
- package/dist/generated/config.generated.js +1 -0
- package/dist/generated/config.generated.js.map +1 -0
- package/dist/generated/guards.generated.cjs +32 -0
- package/dist/generated/guards.generated.cjs.map +1 -0
- package/dist/generated/guards.generated.d.cts +4 -0
- package/dist/generated/guards.generated.d.ts +4 -0
- package/dist/generated/guards.generated.js +8 -0
- package/dist/generated/guards.generated.js.map +1 -0
- package/dist/generated/materialisers.generated.cjs +44 -0
- package/dist/generated/materialisers.generated.cjs.map +1 -0
- package/dist/generated/materialisers.generated.d.cts +4 -0
- package/dist/generated/materialisers.generated.d.ts +4 -0
- package/dist/generated/materialisers.generated.js +18 -0
- package/dist/generated/materialisers.generated.js.map +1 -0
- package/dist/generated/operations.generated.cjs +110 -0
- package/dist/generated/operations.generated.cjs.map +1 -0
- package/dist/generated/operations.generated.d.cts +337 -0
- package/dist/generated/operations.generated.d.ts +337 -0
- package/dist/generated/operations.generated.js +89 -0
- package/dist/generated/operations.generated.js.map +1 -0
- package/dist/generated/preconditions.generated.cjs +44 -0
- package/dist/generated/preconditions.generated.cjs.map +1 -0
- package/dist/generated/preconditions.generated.d.cts +4 -0
- package/dist/generated/preconditions.generated.d.ts +4 -0
- package/dist/generated/preconditions.generated.js +18 -0
- package/dist/generated/preconditions.generated.js.map +1 -0
- package/dist/guards.generated-DKKDKREA.d.ts +14 -0
- package/dist/guards.generated-hrQ1KPNv.d.cts +14 -0
- package/dist/index.cjs +76 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +99 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/materialisers.generated-BsA1uDW8.d.ts +24 -0
- package/dist/materialisers.generated-nMxTAaVi.d.cts +24 -0
- package/dist/preconditions.generated-DblS13Wq.d.cts +24 -0
- package/dist/preconditions.generated-U7YpVh30.d.ts +24 -0
- package/dist/raw-Dj_2rBSQ.d.cts +19 -0
- package/dist/raw-IxCF_bSS.d.ts +19 -0
- package/dist/raw.cjs +34 -0
- package/dist/raw.cjs.map +1 -0
- package/dist/raw.d.cts +4 -0
- package/dist/raw.d.ts +4 -0
- package/dist/raw.js +8 -0
- package/dist/raw.js.map +1 -0
- package/dist/resources-BKlFxjo8.d.cts +56 -0
- package/dist/resources-CYis1_DS.d.ts +56 -0
- package/dist/resources.cjs +39 -0
- package/dist/resources.cjs.map +1 -0
- package/dist/resources.d.cts +3 -0
- package/dist/resources.d.ts +3 -0
- package/dist/resources.js +14 -0
- package/dist/resources.js.map +1 -0
- package/dist/run/inputs.cjs +51 -0
- package/dist/run/inputs.cjs.map +1 -0
- package/dist/run/inputs.d.cts +91 -0
- package/dist/run/inputs.d.ts +91 -0
- package/dist/run/inputs.js +26 -0
- package/dist/run/inputs.js.map +1 -0
- package/dist/sdk.cjs +60 -0
- package/dist/sdk.cjs.map +1 -0
- package/dist/sdk.d.cts +112 -0
- package/dist/sdk.d.ts +112 -0
- package/dist/sdk.js +40 -0
- package/dist/sdk.js.map +1 -0
- package/dist/types.cjs +17 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +74 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +81 -0
- package/src/authoring/FlowBuilderCore.ts +270 -0
- package/src/authoring/handles.ts +81 -0
- package/src/authoring/raw.ts +60 -0
- package/src/authoring/signatureArgs.ts +249 -0
- package/src/client.ts +230 -0
- package/src/discovery.ts +51 -0
- package/src/errors.ts +150 -0
- package/src/examples/approveAndDeposit.ts +84 -0
- package/src/examples/callContract.ts +199 -0
- package/src/examples/config.ts +9 -0
- package/src/examples/consolidateToEth.ts +94 -0
- package/src/examples/consolidateToUsdc.ts +95 -0
- package/src/examples/depositFromProxy.ts +87 -0
- package/src/examples/dustSweep.ts +66 -0
- package/src/examples/lifiSwap.ts +56 -0
- package/src/examples/lifiZap.ts +60 -0
- package/src/examples/rawCallWithArithmetic.ts +78 -0
- package/src/examples/readContractState.ts +77 -0
- package/src/examples/run.ts +168 -0
- package/src/examples/splitAndZap.ts +73 -0
- package/src/examples/splitWithArithmetic.ts +93 -0
- package/src/examples/swapAndZap.ts +72 -0
- package/src/examples/swapToRecipient.ts +109 -0
- package/src/examples/swapWithAllowRevert.ts +119 -0
- package/src/examples/swapWithBalanceCheck.ts +73 -0
- package/src/examples/swapWithOutputValidation.ts +125 -0
- package/src/examples/transferTokens.ts +123 -0
- package/src/examples/untypedOpWithTypedRef.ts +77 -0
- package/src/generated/config.generated.ts +174 -0
- package/src/generated/guards.generated.ts +14 -0
- package/src/generated/materialisers.generated.ts +44 -0
- package/src/generated/operations.generated.ts +568 -0
- package/src/generated/preconditions.generated.ts +43 -0
- package/src/index.ts +149 -0
- package/src/raw.ts +10 -0
- package/src/resources.ts +61 -0
- package/src/run/inputs.ts +117 -0
- package/src/sdk.ts +173 -0
- package/src/types.ts +101 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
|
|
2
|
+
|
|
3
|
+
import { createComposeSdk, materialisers, resources } from '../index.js';
|
|
4
|
+
|
|
5
|
+
import { BASE_URL, OWNER } from './config.js';
|
|
6
|
+
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
|
|
7
|
+
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Swap WETH to USDC with `simulationPolicy: 'allow-revert'`.
|
|
11
|
+
*
|
|
12
|
+
* Demonstrates:
|
|
13
|
+
* - Opting into partial results when simulation detects a revert
|
|
14
|
+
* - The `ComposeCompileResult` discriminated union (`status: 'success' | 'partial'`)
|
|
15
|
+
* - Inspecting `simulationRevert` diagnostics on a partial result
|
|
16
|
+
* - Using `checkOnChainAllowances` to filter already-sufficient approvals
|
|
17
|
+
*
|
|
18
|
+
* When the compiled transaction reverts in simulation:
|
|
19
|
+
* - Default (`simulationPolicy: 'strict'`): the API returns HTTP 422 and the SDK throws.
|
|
20
|
+
* - With `simulationPolicy: 'allow-revert'`: the API returns HTTP 206 and the SDK returns
|
|
21
|
+
* a partial result containing the transaction (without `gasLimit`) and revert diagnostics.
|
|
22
|
+
* The caller can inspect the revert reason and decide whether to submit anyway.
|
|
23
|
+
*/
|
|
24
|
+
export const buildSwapWithAllowRevertExample = (): {
|
|
25
|
+
flow: Flow;
|
|
26
|
+
request: ComposeCompileRequest;
|
|
27
|
+
} => {
|
|
28
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL });
|
|
29
|
+
|
|
30
|
+
const builder = sdk.flow(1, {
|
|
31
|
+
name: 'swap-weth-to-usdc-allow-revert',
|
|
32
|
+
inputs: {
|
|
33
|
+
amountIn: resources.erc20(WETH, 1),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
builder.lifi.swap('swap', {
|
|
38
|
+
bind: { amountIn: builder.inputs.amountIn },
|
|
39
|
+
config: {
|
|
40
|
+
resourceOut: resources.erc20(USDC, 1),
|
|
41
|
+
slippage: 0.03,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const flow = builder.build();
|
|
46
|
+
|
|
47
|
+
// Pass simulationPolicy and checkOnChainAllowances in the run inputs.
|
|
48
|
+
// 'allow-revert' tells the backend to return a partial result instead of
|
|
49
|
+
// a 422 error when the transaction reverts in simulation.
|
|
50
|
+
const request = sdk.request(flow, {
|
|
51
|
+
signer: OWNER,
|
|
52
|
+
inputs: {
|
|
53
|
+
amountIn: materialisers.directDeposit({
|
|
54
|
+
amount: '1000000000000000000',
|
|
55
|
+
}),
|
|
56
|
+
},
|
|
57
|
+
sweepTo: builder.context.sender,
|
|
58
|
+
simulationPolicy: 'allow-revert',
|
|
59
|
+
checkOnChainAllowances: true,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return { flow, request };
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Shows how to handle the `ComposeCompileResult` discriminated union
|
|
67
|
+
* returned by `sdk.client.compile()`.
|
|
68
|
+
*
|
|
69
|
+
* This function is illustrative — it cannot run without a live backend,
|
|
70
|
+
* but demonstrates the branching pattern callers should use.
|
|
71
|
+
*/
|
|
72
|
+
export const handleCompileResult = async (): Promise<void> => {
|
|
73
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL });
|
|
74
|
+
|
|
75
|
+
const builder = sdk.flow(1, {
|
|
76
|
+
inputs: { amountIn: resources.erc20(WETH, 1) },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
builder.lifi.swap('swap', {
|
|
80
|
+
bind: { amountIn: builder.inputs.amountIn },
|
|
81
|
+
config: { resourceOut: resources.erc20(USDC, 1), slippage: 0.03 },
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const result = await builder.compile({
|
|
85
|
+
signer: OWNER,
|
|
86
|
+
inputs: {
|
|
87
|
+
amountIn: materialisers.directDeposit({ amount: '1000000000000000000' }),
|
|
88
|
+
},
|
|
89
|
+
simulationPolicy: 'allow-revert',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (result.status === 'success') {
|
|
93
|
+
// Full success — transactionRequest includes gasLimit.
|
|
94
|
+
const { transactionRequest, userProxy, producedResources } = result;
|
|
95
|
+
void transactionRequest;
|
|
96
|
+
void userProxy;
|
|
97
|
+
void producedResources;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Partial result — simulation reverted but a transaction is still available.
|
|
102
|
+
// transactionRequest omits gasLimit; the caller must estimate gas themselves.
|
|
103
|
+
const { transactionRequest, simulationRevert, error } = result;
|
|
104
|
+
void transactionRequest;
|
|
105
|
+
|
|
106
|
+
// Inspect the revert reason.
|
|
107
|
+
void simulationRevert.code;
|
|
108
|
+
void simulationRevert.rawErrorBytes;
|
|
109
|
+
if (simulationRevert.decodeResult?.errorCandidates) {
|
|
110
|
+
for (const candidate of simulationRevert.decodeResult.errorCandidates) {
|
|
111
|
+
void candidate.decodedErrorSignature;
|
|
112
|
+
void candidate.decodedParams;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// The error envelope from the server.
|
|
117
|
+
void error.kind; // e.g. 'simulation_revert'
|
|
118
|
+
void error.message; // human-readable description
|
|
119
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
|
|
2
|
+
|
|
3
|
+
import { createComposeSdk, materialisers, resources } from '../index.js';
|
|
4
|
+
import type { Address } from '../types.js';
|
|
5
|
+
|
|
6
|
+
import { BASE_URL } from './config.js';
|
|
7
|
+
|
|
8
|
+
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
|
|
9
|
+
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
10
|
+
|
|
11
|
+
export interface SwapWithBalanceCheckInput {
|
|
12
|
+
readonly owner: Address;
|
|
13
|
+
readonly amount: `${bigint}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Swap WETH to USDC via LI.FI, then assert the execution contract holds
|
|
18
|
+
* at least the swapped amount.
|
|
19
|
+
*
|
|
20
|
+
* Demonstrates:
|
|
21
|
+
* - Using builder.context.executionAddress to reference the VM contract
|
|
22
|
+
* - invariant.balanceAtLeast as a post-swap safety check
|
|
23
|
+
* - No hardcoded contract address — the context provides it at compile time
|
|
24
|
+
*/
|
|
25
|
+
export const buildSwapWithBalanceCheck = ({
|
|
26
|
+
owner,
|
|
27
|
+
amount,
|
|
28
|
+
}: SwapWithBalanceCheckInput): {
|
|
29
|
+
flow: Flow;
|
|
30
|
+
request: ComposeCompileRequest;
|
|
31
|
+
} => {
|
|
32
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL });
|
|
33
|
+
|
|
34
|
+
// Declare the flow with a single WETH input on Ethereum mainnet.
|
|
35
|
+
const builder = sdk.flow(1, {
|
|
36
|
+
name: 'swap-with-balance-check',
|
|
37
|
+
inputs: {
|
|
38
|
+
amountIn: resources.erc20(WETH, 1),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Swap WETH → USDC via LI.FI.
|
|
43
|
+
const swapOutputs = builder.lifi.swap('swap', {
|
|
44
|
+
bind: { amountIn: builder.inputs.amountIn },
|
|
45
|
+
config: {
|
|
46
|
+
resourceOut: resources.erc20(USDC, 1),
|
|
47
|
+
slippage: 0.03,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Assert the VM execution contract holds at least the swapped USDC amount.
|
|
52
|
+
// builder.context.executionAddress resolves to the contract at compile time.
|
|
53
|
+
builder.invariant.balanceAtLeast('check-usdc', {
|
|
54
|
+
bind: {
|
|
55
|
+
minimumAmount: swapOutputs.amountOut,
|
|
56
|
+
account: builder.context.executionAddress,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const flow = builder.build();
|
|
61
|
+
|
|
62
|
+
// Build the compile request.
|
|
63
|
+
// directDeposit transfers a fixed amount of WETH into the VM.
|
|
64
|
+
const request = sdk.request(flow, {
|
|
65
|
+
signer: owner,
|
|
66
|
+
inputs: {
|
|
67
|
+
amountIn: materialisers.directDeposit({ amount }),
|
|
68
|
+
},
|
|
69
|
+
sweepTo: builder.context.sender,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return { flow, request };
|
|
73
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
|
|
2
|
+
|
|
3
|
+
import { createComposeSdk, materialisers, resources } from '../index.js';
|
|
4
|
+
import type { Address } from '../types.js';
|
|
5
|
+
|
|
6
|
+
import { BASE_URL } from './config.js';
|
|
7
|
+
|
|
8
|
+
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
|
|
9
|
+
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
10
|
+
|
|
11
|
+
export interface SwapWithOutputValidationInput {
|
|
12
|
+
readonly owner: Address;
|
|
13
|
+
readonly amount: `${bigint}`;
|
|
14
|
+
readonly expectedOut: `${bigint}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Swap WETH to USDC, then validate the output amount against computed
|
|
19
|
+
* slippage bounds.
|
|
20
|
+
*
|
|
21
|
+
* Demonstrates:
|
|
22
|
+
* - core.bpsDown / core.bpsUp to compute lower and upper bounds
|
|
23
|
+
* - core.subtract / core.add for threshold arithmetic
|
|
24
|
+
* - All seven assertion ops for output validation
|
|
25
|
+
*/
|
|
26
|
+
export const buildSwapWithOutputValidation = ({
|
|
27
|
+
owner,
|
|
28
|
+
amount,
|
|
29
|
+
expectedOut: _expectedOut,
|
|
30
|
+
}: SwapWithOutputValidationInput): {
|
|
31
|
+
flow: Flow;
|
|
32
|
+
request: ComposeCompileRequest;
|
|
33
|
+
} => {
|
|
34
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL });
|
|
35
|
+
|
|
36
|
+
const builder = sdk.flow(1, {
|
|
37
|
+
name: 'swap-with-output-validation',
|
|
38
|
+
inputs: {
|
|
39
|
+
amountIn: resources.erc20(WETH, 1),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Swap WETH → USDC.
|
|
44
|
+
const swapOutputs = builder.lifi.swap('swap', {
|
|
45
|
+
bind: { amountIn: builder.inputs.amountIn },
|
|
46
|
+
config: {
|
|
47
|
+
resourceOut: resources.erc20(USDC, 1),
|
|
48
|
+
slippage: 0.03,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Compute slippage bounds from the expected output.
|
|
53
|
+
// Lower bound: expectedOut * (10000 - 300) / 10000 (3% slippage down)
|
|
54
|
+
const lowerBound = builder.core.bpsDown('lower-bound', {
|
|
55
|
+
bind: { value: swapOutputs.amountOut },
|
|
56
|
+
config: { bps: 9700 },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Upper bound: expectedOut * (10000 + 100) / 10000 (1% above expected)
|
|
60
|
+
const upperBound = builder.core.bpsUp('upper-bound', {
|
|
61
|
+
bind: { value: swapOutputs.amountOut },
|
|
62
|
+
config: { bps: 10100 },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Derive a margin value: upper - lower
|
|
66
|
+
const margin = builder.core.subtract('margin', {
|
|
67
|
+
bind: { a: upperBound.result, b: lowerBound.result },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Sanity check: lower + margin should reconstruct the gap
|
|
71
|
+
const reconstructed = builder.core.add('reconstructed', {
|
|
72
|
+
bind: { a: lowerBound.result, b: margin.result },
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Validate: output >= lower bound
|
|
76
|
+
builder.core.assertGte('check-gte-lower', {
|
|
77
|
+
bind: { a: swapOutputs.amountOut, b: lowerBound.result },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Validate: output <= upper bound
|
|
81
|
+
builder.core.assertLte('check-lte-upper', {
|
|
82
|
+
bind: { a: swapOutputs.amountOut, b: upperBound.result },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Validate: output falls within [lower, upper] range
|
|
86
|
+
builder.core.assertInRange('check-range', {
|
|
87
|
+
bind: {
|
|
88
|
+
value: swapOutputs.amountOut,
|
|
89
|
+
min: lowerBound.result,
|
|
90
|
+
max: upperBound.result,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Validate: upper bound > lower bound
|
|
95
|
+
builder.core.assertGt('check-upper-gt-lower', {
|
|
96
|
+
bind: { a: upperBound.result, b: lowerBound.result },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Validate: lower bound < upper bound (inverse check)
|
|
100
|
+
builder.core.assertLt('check-lower-lt-upper', {
|
|
101
|
+
bind: { a: lowerBound.result, b: upperBound.result },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Validate: reconstructed gap equals upper - lower identity
|
|
105
|
+
builder.core.assertEqual('check-reconstruction', {
|
|
106
|
+
bind: { a: reconstructed.result, b: upperBound.result },
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Validate: lower and upper bounds are distinct
|
|
110
|
+
builder.core.assertNotEqual('check-bounds-distinct', {
|
|
111
|
+
bind: { a: lowerBound.result, b: upperBound.result },
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const flow = builder.build();
|
|
115
|
+
|
|
116
|
+
const request = sdk.request(flow, {
|
|
117
|
+
signer: owner,
|
|
118
|
+
inputs: {
|
|
119
|
+
amountIn: materialisers.directDeposit({ amount }),
|
|
120
|
+
},
|
|
121
|
+
sweepTo: builder.context.sender,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return { flow, request };
|
|
125
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
|
|
2
|
+
|
|
3
|
+
import { createComposeSdk, materialisers, resources } from '../index.js';
|
|
4
|
+
import type { Address } from '../types.js';
|
|
5
|
+
|
|
6
|
+
import { BASE_URL } from './config.js';
|
|
7
|
+
|
|
8
|
+
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
9
|
+
|
|
10
|
+
export interface TransferTokensInput {
|
|
11
|
+
readonly owner: Address;
|
|
12
|
+
readonly recipient: Address;
|
|
13
|
+
readonly amount: `${bigint}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Transfer ERC-20 tokens from the proxy to an arbitrary recipient.
|
|
18
|
+
*
|
|
19
|
+
* Demonstrates:
|
|
20
|
+
* - `core.transfer` to move tokens to a recipient address
|
|
21
|
+
* - Binding a scalar handle input (`recipient`) alongside a resource input
|
|
22
|
+
* - Optionally specifying a partial transfer amount via `config.amount`
|
|
23
|
+
* (omit to transfer the full balance)
|
|
24
|
+
* - The `transferred` and `remainder` output ports
|
|
25
|
+
*/
|
|
26
|
+
export const buildTransferTokens = ({
|
|
27
|
+
owner,
|
|
28
|
+
recipient,
|
|
29
|
+
amount,
|
|
30
|
+
}: TransferTokensInput): {
|
|
31
|
+
flow: Flow;
|
|
32
|
+
request: ComposeCompileRequest;
|
|
33
|
+
} => {
|
|
34
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL });
|
|
35
|
+
|
|
36
|
+
const builder = sdk.flow(1, {
|
|
37
|
+
name: 'transfer-usdc',
|
|
38
|
+
inputs: {
|
|
39
|
+
amountIn: resources.erc20(USDC, 1),
|
|
40
|
+
recipient: 'address',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Transfer the full input amount to the recipient.
|
|
45
|
+
// The `transferred` port tracks what was sent; `remainder` tracks what's left.
|
|
46
|
+
builder.core.transfer('send', {
|
|
47
|
+
bind: {
|
|
48
|
+
amount: builder.inputs.amountIn,
|
|
49
|
+
recipient: builder.inputs.recipient,
|
|
50
|
+
},
|
|
51
|
+
config: {},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const flow = builder.build();
|
|
55
|
+
|
|
56
|
+
const request = sdk.request(flow, {
|
|
57
|
+
signer: owner,
|
|
58
|
+
inputs: {
|
|
59
|
+
amountIn: materialisers.directDeposit({ amount }),
|
|
60
|
+
recipient,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return { flow, request };
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Transfer a partial amount, keeping the remainder in the proxy.
|
|
69
|
+
*
|
|
70
|
+
* Demonstrates:
|
|
71
|
+
* - `config.amount` to transfer a specific sub-amount
|
|
72
|
+
* - Using the `remainder` output port for subsequent operations
|
|
73
|
+
*/
|
|
74
|
+
export const buildPartialTransfer = ({
|
|
75
|
+
owner,
|
|
76
|
+
recipient,
|
|
77
|
+
amount,
|
|
78
|
+
}: TransferTokensInput): {
|
|
79
|
+
flow: Flow;
|
|
80
|
+
request: ComposeCompileRequest;
|
|
81
|
+
} => {
|
|
82
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL });
|
|
83
|
+
|
|
84
|
+
const builder = sdk.flow(1, {
|
|
85
|
+
name: 'partial-transfer-usdc',
|
|
86
|
+
inputs: {
|
|
87
|
+
amountIn: resources.erc20(USDC, 1),
|
|
88
|
+
recipient: 'address',
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Transfer a fixed 0.5 USDC (500000 base units at 6 decimals);
|
|
93
|
+
// the remainder stays on the proxy for subsequent operations.
|
|
94
|
+
const out = builder.core.transfer('send-half', {
|
|
95
|
+
bind: {
|
|
96
|
+
amount: builder.inputs.amountIn,
|
|
97
|
+
recipient: builder.inputs.recipient,
|
|
98
|
+
},
|
|
99
|
+
config: { amount: '500000' },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Use the remainder in a swap.
|
|
103
|
+
builder.lifi.swap('swap-remainder', {
|
|
104
|
+
bind: { amountIn: out.remainder },
|
|
105
|
+
config: {
|
|
106
|
+
resourceOut: resources.native(1),
|
|
107
|
+
slippage: 0.03,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const flow = builder.build();
|
|
112
|
+
|
|
113
|
+
const request = sdk.request(flow, {
|
|
114
|
+
signer: owner,
|
|
115
|
+
inputs: {
|
|
116
|
+
amountIn: materialisers.directDeposit({ amount }),
|
|
117
|
+
recipient,
|
|
118
|
+
},
|
|
119
|
+
sweepTo: builder.context.sender,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return { flow, request };
|
|
123
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec';
|
|
2
|
+
|
|
3
|
+
import { createComposeSdk, materialisers, raw, resources } from '../index.js';
|
|
4
|
+
import type { Address } from '../types.js';
|
|
5
|
+
|
|
6
|
+
import { BASE_URL } from './config.js';
|
|
7
|
+
|
|
8
|
+
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
|
|
9
|
+
// Example custom vault that the typed builder doesn't cover yet
|
|
10
|
+
const CUSTOM_VAULT = '0x1111111111111111111111111111111111111111';
|
|
11
|
+
|
|
12
|
+
export interface UntypedOpWithTypedRefInput {
|
|
13
|
+
readonly owner: Address;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Insert an untyped operation node for an unsupported operation, then feed
|
|
18
|
+
* its output into typed builder methods using `raw.ref<T>()`.
|
|
19
|
+
*
|
|
20
|
+
* Demonstrates:
|
|
21
|
+
* - `builder.untypedOp()` to add a node the typed API doesn't cover
|
|
22
|
+
* (e.g. a custom vault query).
|
|
23
|
+
* - `raw.ref<'uint256'>()` to reference the untyped node's output in a typed
|
|
24
|
+
* `Bindable<'uint256'>` slot — the explicit type parameter is required
|
|
25
|
+
* so the compiler verifies the ref is used in a compatible slot.
|
|
26
|
+
* - Mixing untyped and typed nodes in the same flow.
|
|
27
|
+
*/
|
|
28
|
+
export const buildUntypedOpWithTypedRef = ({
|
|
29
|
+
owner,
|
|
30
|
+
}: UntypedOpWithTypedRefInput): {
|
|
31
|
+
flow: Flow;
|
|
32
|
+
request: ComposeCompileRequest;
|
|
33
|
+
} => {
|
|
34
|
+
const sdk = createComposeSdk({ baseUrl: BASE_URL });
|
|
35
|
+
|
|
36
|
+
const builder = sdk.flow(1, {
|
|
37
|
+
name: 'untyped-op-with-typed-ref',
|
|
38
|
+
inputs: {
|
|
39
|
+
amountIn: resources.erc20(USDC, 1),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Insert an untyped node for a custom vault query. untypedOp returns void —
|
|
44
|
+
// the typed builder has no knowledge of this node's outputs.
|
|
45
|
+
builder.untypedOp('vault-query', 'custom.vaultQuery', {
|
|
46
|
+
bind: { amount: { $ref: 'input.amountIn' } },
|
|
47
|
+
config: { target: CUSTOM_VAULT },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Use raw.ref to bridge the untyped node's output into a typed operation.
|
|
51
|
+
// The type parameter <'uint256'> tells the compiler this ref produces a
|
|
52
|
+
// uint256, so it's accepted by Bindable<'uint256'> slots.
|
|
53
|
+
const vaultResult = raw.ref<'uint256'>('vault-query.result');
|
|
54
|
+
|
|
55
|
+
// Feed the raw result into typed arithmetic — the compiler checks that
|
|
56
|
+
// vaultResult (typed as uint256) is compatible with the uint256 bind slot.
|
|
57
|
+
const scaled = builder.core.multiply('scale', {
|
|
58
|
+
bind: { a: vaultResult, b: vaultResult },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Continue with fully typed operations downstream.
|
|
62
|
+
builder.core.assertGte('check-min', {
|
|
63
|
+
bind: { a: scaled.result, b: builder.inputs.amountIn },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const flow = builder.build();
|
|
67
|
+
|
|
68
|
+
const request = sdk.request(flow, {
|
|
69
|
+
signer: owner,
|
|
70
|
+
inputs: {
|
|
71
|
+
amountIn: materialisers.directDeposit({ amount: '1000000' }),
|
|
72
|
+
},
|
|
73
|
+
sweepTo: builder.context.sender,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return { flow, request };
|
|
77
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// AUTO-GENERATED from manifest.json — do not edit by hand.
|
|
2
|
+
// Regenerate with: yarn workspace @lifi/composer-sdk generate
|
|
3
|
+
|
|
4
|
+
import type { Address, IntegerStringInput } from '../types.js';
|
|
5
|
+
|
|
6
|
+
export interface CoreSplitConfig {
|
|
7
|
+
bps: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* a positive number
|
|
12
|
+
*/
|
|
13
|
+
export type Positive = number;
|
|
14
|
+
|
|
15
|
+
export interface LifiSwapConfig {
|
|
16
|
+
resourceOut:
|
|
17
|
+
| {
|
|
18
|
+
kind: 'native';
|
|
19
|
+
chainId: Positive;
|
|
20
|
+
}
|
|
21
|
+
| {
|
|
22
|
+
kind: 'erc20';
|
|
23
|
+
token: Address;
|
|
24
|
+
chainId: Positive;
|
|
25
|
+
};
|
|
26
|
+
slippage?: number;
|
|
27
|
+
timing?: {
|
|
28
|
+
swapStepTimingStrategies?: {
|
|
29
|
+
strategy: 'minWaitTime';
|
|
30
|
+
minWaitTimeMs: number;
|
|
31
|
+
startingExpectedResults: number;
|
|
32
|
+
reduceEveryMs: number;
|
|
33
|
+
}[];
|
|
34
|
+
routeTimingStrategies?: {
|
|
35
|
+
strategy: 'minWaitTime';
|
|
36
|
+
minWaitTimeMs: number;
|
|
37
|
+
startingExpectedResults: number;
|
|
38
|
+
reduceEveryMs: number;
|
|
39
|
+
}[];
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface LifiZapConfig {
|
|
44
|
+
resourceOut:
|
|
45
|
+
| {
|
|
46
|
+
kind: 'native';
|
|
47
|
+
chainId: Positive;
|
|
48
|
+
}
|
|
49
|
+
| {
|
|
50
|
+
kind: 'erc20';
|
|
51
|
+
token: Address;
|
|
52
|
+
chainId: Positive;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CoreBpsDownConfig {
|
|
57
|
+
bps: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface CoreBpsUpConfig {
|
|
61
|
+
bps: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface CoreApproveConfig {
|
|
65
|
+
spender: Address;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface CoreBalanceOfConfig {
|
|
69
|
+
token: Address;
|
|
70
|
+
owner?: Address;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface CoreTransferConfig {
|
|
74
|
+
amount?: IntegerStringInput;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface CoreCallConfig {
|
|
78
|
+
target: Address;
|
|
79
|
+
functionSignature: string;
|
|
80
|
+
callType?: 'Call' | 'ValueCall';
|
|
81
|
+
args?: Unknown[];
|
|
82
|
+
approvals?: {
|
|
83
|
+
spender: Address;
|
|
84
|
+
}[];
|
|
85
|
+
}
|
|
86
|
+
export interface Unknown {
|
|
87
|
+
[k: string]: unknown;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface CoreStaticCallConfig {
|
|
91
|
+
target: Address;
|
|
92
|
+
functionSignature: string;
|
|
93
|
+
args?: Unknown[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface CoreRawCallConfig {
|
|
97
|
+
target: Address;
|
|
98
|
+
calldata: string;
|
|
99
|
+
callType?: 'Call' | 'StaticCall' | 'ValueCall' | 'DelegateCall';
|
|
100
|
+
resultType?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface CoreInvokeConfig {
|
|
104
|
+
target: Address;
|
|
105
|
+
functionSignature: string;
|
|
106
|
+
callType?: 'Call' | 'ValueCall';
|
|
107
|
+
args?: Unknown[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface CoreAsResourceConfig {
|
|
111
|
+
resource:
|
|
112
|
+
| {
|
|
113
|
+
kind: 'native';
|
|
114
|
+
chainId: Positive;
|
|
115
|
+
}
|
|
116
|
+
| {
|
|
117
|
+
kind: 'erc20';
|
|
118
|
+
token: Address;
|
|
119
|
+
chainId: Positive;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface CorePeekConfig {
|
|
124
|
+
target: Address;
|
|
125
|
+
functionSignature: string;
|
|
126
|
+
args?: string[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* a number between 0 and 10000
|
|
131
|
+
*/
|
|
132
|
+
export type Between010000 = number;
|
|
133
|
+
|
|
134
|
+
export interface SlippageGuardConfig {
|
|
135
|
+
port: string;
|
|
136
|
+
bps: Between010000;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface BalanceOfMaterialiserConfig {
|
|
140
|
+
owner: Address;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export type DirectDepositMaterialiserConfig =
|
|
144
|
+
| {
|
|
145
|
+
amount: IntegerStringInput;
|
|
146
|
+
allowNonExact?: false;
|
|
147
|
+
}
|
|
148
|
+
| {
|
|
149
|
+
amount?: IntegerStringInput;
|
|
150
|
+
allowNonExact: true;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export interface CallMaterialiserConfig {
|
|
154
|
+
target: Address;
|
|
155
|
+
calldata: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface Erc20BalancePreconditionConfig {
|
|
159
|
+
wallet: Address;
|
|
160
|
+
token: Address;
|
|
161
|
+
balance: IntegerStringInput;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface NativeBalancePreconditionConfig {
|
|
165
|
+
wallet: Address;
|
|
166
|
+
balance: IntegerStringInput;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface Erc20AllowancePreconditionConfig {
|
|
170
|
+
owner: Address;
|
|
171
|
+
spender: Address;
|
|
172
|
+
token: Address;
|
|
173
|
+
allowance: IntegerStringInput;
|
|
174
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// AUTO-GENERATED from manifest.json — do not edit by hand.
|
|
2
|
+
// Regenerate with: yarn workspace @lifi/composer-sdk generate
|
|
3
|
+
|
|
4
|
+
import type { TypedGuard } from '../types.js';
|
|
5
|
+
|
|
6
|
+
import type { SlippageGuardConfig } from './config.generated.js';
|
|
7
|
+
|
|
8
|
+
/** Assert that output amounts are within a slippage tolerance in basis points */
|
|
9
|
+
export const slippage = <P extends string>(
|
|
10
|
+
config: Omit<SlippageGuardConfig, 'port'> & { port: P },
|
|
11
|
+
): TypedGuard<P> => ({
|
|
12
|
+
kind: 'slippage',
|
|
13
|
+
...config,
|
|
14
|
+
});
|