Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use permit for simple fulfills #3

Closed
wants to merge 1 commit into from
Closed
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
3 changes: 2 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ ETHEREUM_RPC="https://rpc.ankr.com/eth"
OPTIMISM_RPC="https://mainnet.optimism.io"
POLYGON_RPC="https://polygon-rpc.com/"

DISABLE_UNLOCKER="false"
DISABLE_UNLOCKER="false"
USE_PERMIT="true"
6 changes: 3 additions & 3 deletions src/config/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export type MayanEndpoints = {
};

export const mayanEndpoints: MayanEndpoints = {
explorerWsAddress: process.env.EXPLORER_WS_ADDRESS || 'https://explorer-api.mayan.finance',
explorerWsAddress: process.env.EXPLORER_WS_ADDRESS || 'https://test-explorer-api.mayan.finance',
relayerWsAddress: process.env.RELAYER_WS_ADDRESS || 'https://swifdt-relayer-api.mayan.finance',
explorerApiUrl: process.env.EXPLORER_API_URL || 'https://explorer-api.mayan.finance',
priceApiUrl: process.env.PRICE_API_URL || 'https://price-api.mayan.finance',
explorerApiUrl: process.env.EXPLORER_API_URL || 'https://test-explorer-api.mayan.finance',
priceApiUrl: process.env.PRICE_API_URL || 'https://test-price-api.mayan.finance',
lutApiUrl: process.env.LUT_API_URL || 'https://lut-api.mayan.finance',
refreshTokenIntervalSeconds: Number(process.env.REFRESH_TOKEN_INTERVAL_SECONDS) || 120,
};
1 change: 1 addition & 0 deletions src/config/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface SwiftFeeParams {
}

export type GlobalConfig = {
usePermit: boolean;
auctionTimeSeconds: number;
batchUnlockThreshold: number; // Optimal Number of swaps to select for unlocking
singleBatchChainIds: number[]; // Expensive chain-ids that use direct vaa post instead of batch (e.g ethereum)
Expand Down
2 changes: 1 addition & 1 deletion src/driver/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ export class DriverService {

const fulfillAmount = await this.simpleFulfillerCfg.fulfillAmount(swap, effectiveAmountIn, expenses);

await this.evmFulFiller.simpleFulfill(swap, fulfillAmount, toToken);
await this.evmFulFiller.fulfillSimple(swap, fulfillAmount, toToken);
}

async getSimpleFulfillIxsPackage(swap: Swap): Promise<TransactionInstruction[]> {
Expand Down
199 changes: 146 additions & 53 deletions src/driver/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import { Token, TokenList } from '../config/tokens';
import { WalletConfig } from '../config/wallet';
import { SWAP_STATUS, Swap } from '../swap.dto';
import { tryNativeToHexString, tryNativeToUint8Array } from '../utils/buffer';
import { getErc20Allowance, getErc20Balance, getEthBalance, giveErc20Allowance } from '../utils/erc20';
import {
getErc20Allowance,
getErc20Balance,
getEthBalance,
getPermitSignature,
giveErc20Allowance,
} from '../utils/erc20';
import { EvmProviders } from '../utils/evm-providers';
import { getSuggestedOverrides, getTypicalBlocksToConfirm } from '../utils/evm-trx';
import logger from '../utils/logger';
Expand Down Expand Up @@ -42,7 +48,19 @@ export class EvmFulfiller {
}

async init() {
await this.lazySetAllowances();
try {
if (this.gConf.usePermit) {
logger.info('Permit is used for fulfilling ERC20 and no allowance was set');
} else {
await this.lazySetAllowances();
}
} catch (e) {
logger.error(`Error setting allowances: ${e}`);
}
}

private getDummySwapCallData(targetChain: number) {
return this.swiftInterface.encodeFunctionData('paused', []);
}

private async lazySetAllowances() {
Expand Down Expand Up @@ -126,6 +144,107 @@ export class EvmFulfiller {
]);
}

private async generatePermitParams(
tokenContract: string,
rawAmountIn64: bigint,
targetChain: number,
): Promise<any> {
let permitParams: any = {
value: 12313131313n,
deadline: 12313131313n,
v: 12,
r: Buffer.alloc(32),
s: Buffer.alloc(32),
}; // If we use allowance these params don't matter
if (this.gConf.usePermit) {
const absoluteTargetChain = WhChainIdToEvm[targetChain];
permitParams = await getPermitSignature(
this.walletHelper.getDriverWallet(targetChain),
rawAmountIn64,
absoluteTargetChain,
tokenContract,
this.evmProviders[targetChain],
this.contractsConfig.evmFulfillHelpers[targetChain],
60,
);
}

return permitParams;
}

async fulfillSimple(swap: Swap, availableAmountIn: number, toToken: Token) {
const [chosenDriverToken, simpleFulfillData] = await this.generateFulfillSimpleData(
swap,
availableAmountIn,
toToken,
);
const amountIn64 = ethers.parseUnits(
availableAmountIn.toFixed(chosenDriverToken.decimals),
chosenDriverToken.decimals,
);

const targetChain = swap.destChain;
const networkFeeData: ethers.FeeData = await this.evmProviders[targetChain].getFeeData();

const overrides = await getSuggestedOverrides(targetChain, networkFeeData);
const nativeCurrency = this.tokenList.nativeTokens[targetChain];

let gasDrop = swap.gasDrop64;
if (nativeCurrency.decimals > 8) {
gasDrop = gasDrop * 10n ** (BigInt(nativeCurrency.decimals) - 8n);
}
overrides['value'] = gasDrop;

if (chosenDriverToken.contract === ethers.ZeroAddress) {
overrides['value'] = overrides['value'] + ethers.parseUnits(availableAmountIn.toFixed(18), 18);
}

let fulfillTx: ethers.TransactionResponse;
if (chosenDriverToken.contract === ethers.ZeroAddress) {
logger.info(`Sending swap fulfill with fulfillWithEth for tx=${swap.sourceTxHash}`);
overrides['value'] = overrides['value'] + amountIn64;

fulfillTx = await this.walletHelper.getFulfillHelperWriteContract(swap.destChain).fulfillWithEth(
amountIn64,
toToken.contract,
this.contractsConfig.contracts[targetChain], // we don't need swap just set a read only contract
this.getDummySwapCallData(targetChain),
this.contractsConfig.contracts[targetChain],
simpleFulfillData,
overrides,
);
} else {
logger.info(`Sending swap fulfill with fulfillWithERC20 for tx=${swap.sourceTxHash}`);

const permitParams = await this.generatePermitParams(chosenDriverToken.contract, amountIn64, targetChain);

fulfillTx = await this.walletHelper.getFulfillHelperWriteContract(swap.destChain).fulfillWithERC20(
chosenDriverToken.contract,
amountIn64,
toToken.contract,
this.contractsConfig.contracts[targetChain], // we don't need swap just set a read only contract
this.getDummySwapCallData(targetChain),
this.contractsConfig.contracts[targetChain],
simpleFulfillData,
permitParams,
overrides,
);
}

logger.info(`Waiting for simple-fulfill on EVM for ${swap.sourceTxHash} via: ${fulfillTx.hash}`);
const tx = await this.evmProviders[targetChain].waitForTransaction(
fulfillTx.hash,
getTypicalBlocksToConfirm(targetChain),
60_000,
);

if (!tx || tx.status !== 1) {
throw new Error(`Fulfill auction on evm reverted for ${swap.sourceTxHash} via: ${tx?.hash}`);
} else {
swap.status = SWAP_STATUS.ORDER_SETTLED;
}
}

async fulfillAuction(
swap: Swap,
availableAmountIn: number,
Expand Down Expand Up @@ -182,23 +301,21 @@ export class EvmFulfiller {
} else {
logger.info(`Sending swap fulfill with fulfillWithERC20 for tx=${swap.sourceTxHash}`);

fulfillTx = await this.walletHelper.getFulfillHelperWriteContract(swap.destChain).fulfillWithERC20(
driverToken.contract,
amountIn64,
toToken.contract,
swapParams.evmRouterAddress,
swapParams.evmRouterCalldata,
this.contractsConfig.contracts[targetChain],
swiftCallData,
{
value: 12313131313n,
deadline: 12313131313n,
v: 12,
r: Buffer.alloc(32),
s: Buffer.alloc(32),
}, // permit. doesnt matter because we already gave allowance
overrides,
);
const permitParams = await this.generatePermitParams(driverToken.contract, amountIn64, targetChain);

fulfillTx = await this.walletHelper
.getFulfillHelperWriteContract(swap.destChain)
.fulfillWithERC20(
driverToken.contract,
amountIn64,
toToken.contract,
swapParams.evmRouterAddress,
swapParams.evmRouterCalldata,
this.contractsConfig.contracts[targetChain],
swiftCallData,
permitParams,
overrides,
);
}

logger.info(`Waiting for auction-fulfill on EVM for ${swap.sourceTxHash} via: ${fulfillTx.hash}`);
Expand Down Expand Up @@ -352,7 +469,11 @@ export class EvmFulfiller {
}
}

async simpleFulfill(swap: Swap, availableAmountIn: number, toToken: Token) {
private async generateFulfillSimpleData(
swap: Swap,
availableAmountIn: number,
toToken: Token,
): Promise<[Token, string]> {
const targetChain = swap.destChain;
const driverTokens = [this.tokenList.getNativeUsdc(targetChain), this.tokenList.nativeTokens[targetChain]];

Expand Down Expand Up @@ -392,21 +513,6 @@ export class EvmFulfiller {
throw new Error(`Not enough balance for and can not fullfill ${toToken.contract}`);
}

const networkFeeData: ethers.FeeData = await this.evmProviders[targetChain].getFeeData();

const overrides = await getSuggestedOverrides(targetChain, networkFeeData);
const nativeCurrency = this.tokenList.nativeTokens[targetChain];

let gasDrop = swap.gasDrop64;
if (nativeCurrency.decimals > 8) {
gasDrop = gasDrop * 10n ** (BigInt(nativeCurrency.decimals) - 8n);
}
overrides['value'] = gasDrop;

if (chosenDriverToken.contract === ethers.ZeroAddress) {
overrides['value'] = overrides['value'] + ethers.parseUnits(availableAmountIn.toFixed(18), 18);
}

const fromToken = swap.fromToken;
const fromNormalizedDecimals = Math.min(WORMHOLE_DECIMALS, fromToken.decimals);

Expand Down Expand Up @@ -440,9 +546,9 @@ export class EvmFulfiller {

const batch = this.gConf.singleBatchChainIds.includes(+swap.destChain) ? false : true; // batch-post except for eth and expensive chains

const fulfillTx: ethers.TransactionResponse = await this.walletHelper
.getWriteContract(swap.destChain)
.fulfillSimple(
return [
chosenDriverToken,
this.swiftInterface.encodeFunctionData('fulfillSimple', [
ethers.parseUnits(availableAmountIn.toFixed(chosenDriverToken.decimals), chosenDriverToken.decimals),
orderHashHex,
srcChain,
Expand All @@ -465,20 +571,7 @@ export class EvmFulfiller {
},
unlockAddress32,
batch,
overrides,
);

logger.info(`Wait simple-fulfill on evm confirm for ${swap.sourceTxHash} via: ${fulfillTx.hash}`);
const tx = await this.evmProviders[targetChain].waitForTransaction(
fulfillTx.hash,
getTypicalBlocksToConfirm(targetChain),
60_000,
);

if (!tx || tx.status !== 1) {
throw new Error(`Fulfilling on evm tx reverted sourceTx: ${swap.sourceTxHash}, ${tx?.hash}`);
} else {
swap.status = SWAP_STATUS.ORDER_SETTLED;
}
]),
];
}
}
5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ export async function main() {
rpcConfig.wormholeGuardianRpcs = initialDynamicConfig.wormholeGuardianRpcs.split(',');

const globalConfig: GlobalConfig = {
auctionTimeSeconds: 0 || initialDynamicConfig.auctionTimeSeconds, // TODO: remove hardcode values
batchUnlockThreshold: 1 || initialDynamicConfig.batchUnlockThreshold,
usePermit: process.env.USE_PERMIT === 'true',
auctionTimeSeconds: 20 || initialDynamicConfig.auctionTimeSeconds, // TODO: remove hardcode values
batchUnlockThreshold: initialDynamicConfig.batchUnlockThreshold,
registerInterval: initialDynamicConfig.registerInterval,
scheduleUnlockInterval: initialDynamicConfig.scheduleUnlockInterval,
singleBatchChainIds: initialDynamicConfig.singleBatchChainIds.split(',').map((x) => +x),
Expand Down
Loading