Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9b4092b
Added optional intentExecutionTypes parameter to request quotes for a…
dirkpage Mar 14, 2025
1cfcf74
Changes to quote response, open quoting client now parses quote respo…
dirkpage Mar 27, 2025
832e881
Removing applyQuoteToIntent method from RoutesService
dirkpage Apr 3, 2025
dc817dc
Separating out formatting and parsing functions, added initiateGasles…
dirkpage Apr 3, 2025
e651828
Updated exported types
dirkpage Apr 3, 2025
3cb7575
Refactored provided selector function
dirkpage Apr 3, 2025
85b452f
Refactoring selectCheapestQuote
dirkpage Apr 4, 2025
6bd8541
Adding salts in quote responses
dirkpage Apr 4, 2025
7e12e57
Fixes from preprod testing
dirkpage Apr 15, 2025
109ce6f
Fixing SDK for gasless initiation with permit and permit2
dirkpage Apr 18, 2025
bfbcea5
Fixes, refactoring submitGaslessIntent to initiateGaslessIntent
dirkpage Apr 21, 2025
58e072b
Method comment improvements
dirkpage Apr 21, 2025
6f4325e
Adding reverse quote functionality, initial implementation of gasless…
dirkpage Apr 22, 2025
53bdd8e
Updating SDK to v1
dirkpage Apr 23, 2025
6b321f3
Merge branch 'main' of https://github.com/eco/toolkit into gasless-in…
dirkpage Apr 23, 2025
fbba8a1
Publishing alpha for testing
dirkpage Apr 23, 2025
362b79b
Added publish-beta and publish-alpha scripts
dirkpage Apr 23, 2025
733572f
Exporting quote selector result from SDK
dirkpage Apr 28, 2025
0609484
Updating README quick start and adding gasless initiation walkthrough
dirkpage Apr 28, 2025
764a4fe
Updating to use v2 quotes endpoints and publishing alpha 2
dirkpage Apr 29, 2025
97dd100
Adding getFee, reducing sdk index.ts files
dirkpage Apr 29, 2025
96662c3
Removing getFee
dirkpage May 16, 2025
e7edb11
Merge branch 'main' of https://github.com/eco/toolkit into gasless-in…
dirkpage May 26, 2025
945c405
Updating SDK alpha version
dirkpage May 26, 2025
3bf7aed
Merge branch 'main' of https://github.com/eco/toolkit into gasless-in…
dirkpage Jun 25, 2025
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
2 changes: 1 addition & 1 deletion apps/sdk-demo/src/components/edit-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function EditConfig() {
return (
<div className="m-4">
<div className="w-full border-b-1 pb-4">
<span className="text-2xl">Edit Config</span>
<span className="text-2xl">Configure:</span>
<div className="grid grid-cols-2 gap-4">
<div className="flex flex-col gap-2">
<div className="flex gap-2">
Expand Down
41 changes: 41 additions & 0 deletions apps/sdk-demo/src/utils/abis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export const PermitAbi = [
{
inputs: [{ internalType: 'address', name: 'owner', type: 'address' }],
name: 'nonces',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'version',
outputs: [{ internalType: 'string', name: '', type: 'string' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'name',
outputs: [{ internalType: 'string', name: '', type: 'string' }],
stateMutability: 'view',
type: 'function',
},
];

export const Permit2Abi = [
{
inputs: [
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'address', name: '', type: 'address' },
{ internalType: 'address', name: '', type: 'address' },
],
name: 'allowance',
outputs: [
{ internalType: 'uint160', name: 'amount', type: 'uint160' },
{ internalType: 'uint48', name: 'expiration', type: 'uint48' },
{ internalType: 'uint48', name: 'nonce', type: 'uint48' },
],
stateMutability: 'view',
type: 'function',
},
] as const;
12 changes: 12 additions & 0 deletions apps/sdk-demo/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,15 @@ export function getAvailableStables(chain: RoutesSupportedChainId): MyTokenConfi
export function findTokenByAddress(chain: RoutesSupportedChainId, address: string): MyTokenConfig | undefined {
return getAvailableStables(chain).find((token) => token.address === address)
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const replaceBigIntsWithStrings = (obj: any): any => {
if (typeof obj === "bigint") return obj.toString();
if (Array.isArray(obj))
return obj.map((x) => replaceBigIntsWithStrings(x));
if (obj && typeof obj === "object")
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, replaceBigIntsWithStrings(v)]),
);
return obj;
};
144 changes: 144 additions & 0 deletions apps/sdk-demo/src/utils/permit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { Hex } from "viem";
import { signTypedData } from "@wagmi/core";
import { config } from "../wagmi";
import { Permit2DataDetails } from "@eco-foundation/routes-sdk";
import { stableAddresses } from "@eco-foundation/routes-sdk";

// Global permit2 address (same across all chains)
export const PERMIT2_ADDRESS: Hex = "0x000000000022D473030F116dDEE9F6B43aC78BA3";

/**
* Checks if a token is USDC
* @param address Token address
* @returns Boolean indicating if token is USDC
*/
export function isUSDC(address: Hex): boolean {
// Check against known USDC addresses by lowercase comparison
const usdcAddresses = Object.values(stableAddresses)
.map(tokens => tokens.USDC)
.filter(Boolean)
.map(addr => addr?.toLowerCase());

return usdcAddresses.includes(address.toLowerCase());
}

/**
* Signs a permit for a given ERC-2612 ERC20 token using wagmi's signTypedData
*/
export type SignPermitProps = {
/** Address of the token to approve */
contractAddress: Hex
/** Name of the token to approve.
* Corresponds to the `name` method on the ERC-20 contract. Please note this must match exactly byte-for-byte */
erc20Name: string
/** Owner of the tokens. Usually the currently connected address. */
ownerAddress: Hex
/** Address to grant allowance to */
spenderAddress: Hex
/** Expiration of this approval, in SECONDS */
deadline: bigint
/** Numerical chainId of the token contract */
chainId: number
/** Defaults to 1. Some tokens need a different version, check the [PERMIT INFORMATION](https://github.com/vacekj/wagmi-permit/blob/main/PERMIT.md) for more information */
permitVersion?: string
/** Permit nonce for the specific address and token contract. You can get the nonce from the `nonces` method on the token contract. */
nonce: bigint
/** Amount to approve */
value: bigint
}

export async function signPermit({
contractAddress,
erc20Name,
ownerAddress,
spenderAddress,
value,
deadline,
nonce,
chainId,
permitVersion = "1",
}: SignPermitProps): Promise<Hex> {
const types = {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
};

const domainData = {
name: erc20Name,
version: permitVersion,
chainId: chainId,
verifyingContract: contractAddress,
};

const message = {
owner: ownerAddress,
spender: spenderAddress,
value,
nonce,
deadline,
};

return signTypedData(config, {
domain: domainData,
message,
primaryType: 'Permit',
types,
});
}

export type SignPermit2Props = {
chainId: number
expiration: bigint
spender: Hex
details: Permit2DataDetails[]
}

export async function signPermit2({
chainId,
expiration,
spender,
details,
}: SignPermit2Props): Promise<Hex> {
const types = {
PermitDetails: [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint160' },
{ name: 'expiration', type: 'uint48' },
{ name: 'nonce', type: 'uint48' },
],
PermitSingle: [
{ name: 'details', type: 'PermitDetails' },
{ name: 'spender', type: 'address' },
{ name: 'sigDeadline', type: 'uint256' },
],
PermitBatch: [
{ name: 'details', type: 'PermitDetails[]' },
{ name: 'spender', type: 'address' },
{ name: 'sigDeadline', type: 'uint256' }
],
};

const domainData = {
name: "Permit2",
chainId: chainId,
verifyingContract: PERMIT2_ADDRESS,
};

const message = {
details: details.length > 1 ? details : details[0],
spender,
sigDeadline: expiration,
};

return signTypedData(config, {
domain: domainData,
message,
primaryType: details.length > 1 ? 'PermitBatch' : 'PermitSingle',
types,
});
}
42 changes: 34 additions & 8 deletions apps/sdk-demo/src/views/demo/components/create-intent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import { chains } from "../../../wagmi";
type Props = {
routesService: RoutesService,
onNewIntent: (intent: IntentType, isNative: boolean) => void,
quoteType: "receive" | "spend",
setQuoteType: (type: "receive" | "spend") => void
onIntentCleared?: () => void
}

export default function CreateIntent({
routesService,
onNewIntent,
quoteType,
setQuoteType,
onIntentCleared
}: Props) {
const { address } = useAccount();
Expand Down Expand Up @@ -106,7 +110,7 @@ export default function CreateIntent({
creator: address,
originChainID: effectiveOriginChain,
spendingToken: originToken,
spendingTokenLimit: balance,
spendingTokenLimit: quoteType === "receive" ? balance : BigInt(amount),
destinationChainID: effectiveDestinationChain,
receivingToken: destinationToken,
amount: BigInt(amount),
Expand All @@ -127,7 +131,7 @@ export default function CreateIntent({
setIsIntentValid(false)
onIntentCleared?.()
}
}, [isNativeIntent, balance, nativeBalance, address, effectiveOriginChain, originToken, effectiveDestinationChain, destinationToken, amount, recipient, prover, onNewIntent, onIntentCleared, routesService]);
}, [isNativeIntent, balance, nativeBalance, address, effectiveOriginChain, originToken, effectiveDestinationChain, destinationToken, amount, recipient, prover, onNewIntent, onIntentCleared, routesService, quoteType]);

const originTokensAvailable = useMemo(() => effectiveOriginChain ? getAvailableStables(effectiveOriginChain) : [], [effectiveOriginChain]);
const destinationTokensAvailable = useMemo(() => effectiveDestinationChain ? getAvailableStables(effectiveDestinationChain) : [], [effectiveDestinationChain]);
Expand Down Expand Up @@ -301,13 +305,35 @@ export default function CreateIntent({
</>
)}
</div>

<div className="flex flex-col gap-1 p-1 border-1">
<span className="text-xl">Desired Amount {isNativeIntent ? '(in wei)' : ''}</span>
<span className="text-xl">Amount {isNativeIntent ? '(in wei)' : ''}</span>
<input type="number" className="border-1" value={amount} onChange={(e) => setAmount(e.target.value)} />
{amount && isNativeIntent && <span className="text-sm italic">({formatUnits(BigInt(amount), 18)} {nativeBalance?.symbol || 'ETH'})</span>}
{amount && !isNativeIntent && decimals && <span className="text-sm italic">({formatUnits(BigInt(amount), decimals)})</span>}
</div>
<div className="flex gap-2 mb-2">
<label className="flex items-center gap-1">
<input
type="radio"
name="quoteType"
value="receive"
checked={quoteType === "receive"}
onChange={() => setQuoteType("receive")}
/>
<span>Receive this amount</span>
</label>
|
<label className="flex items-center gap-1">
<input
type="radio"
name="quoteType"
value="spend"
checked={quoteType === "spend"}
onChange={() => setQuoteType("spend")}
/>
<span>Spend this amount</span>
</label>
</div>
</div >

<div className="flex flex-col gap-1 p-1 border-1">
<span className="text-xl">Recipient</span>
Expand All @@ -322,7 +348,7 @@ export default function CreateIntent({
<option value={"MetaProver"}>Meta Prover</option>
</select>
</div>
</div>
</div >
<div className="h-full relative">
<pre className="h-full">
{isNativeIntent ?
Expand Down Expand Up @@ -365,7 +391,7 @@ export default function CreateIntent({
)}
</div>
</div>
</div>
</div>
</div >
</div >
)
}
Loading