Gateway 和 Uniswap
gateway通過用ethers(https://github.com/ethers-io/ethers.js)與ETH網絡交互
使用@uniswap/sdk(https://github.com/Uniswap/uniswap-v2-sdk)來實現(xiàn)與uninswap的交互
EthereumService 組件(ethereumService)
ethereum.ts
封裝了調用 ether 庫針對 eth 網絡的操作
getETHBalance
從錢包中檢查 ETH 余額
getERC20Balance
從合約中檢查 ERC20 的余額
getERC20Allowance
檢查 spender 允許從錢包轉移的數(shù)目
approveERC20
授權spender從錢包轉移ERC20代幣
deposit
給 ERC20 地址轉幣员淫,暫時沒人用這個 api
getERC20TokenAddress
獲取 ERC20 代幣地址
getERC20TokenByAddress
通過地址獲取 ERC20 代幣
getWallet
獲取錢包信息
getTransactionReceipt
通過 txHash 獲得交易信息
Uniswap 組件(uni)
uniswap.js
封裝了針對 uniswap 的操作荡碾,比如下單命贴,獲取交易對等操作
fetch_route
: 獲取交易路徑精盅,主要在獲取兌換價格priceSwapIn
時候使用吞获,因為有的幣可能需要經過>1次兌換才能換到目標幣種渔嚷,這種情況下就需要獲取交易路徑。
extend_update_pairs
: 在獲取兌換價格priceSwapIn
的時候會使用媳握,用來更新交換幣對的擴展的屬性碱屁。
update_pairs
: 定時執(zhí)行更新維護幣對列表,主要只維護幣對蛾找,擴展信息交由獲取價格時候調用 extend_update_pairs
實現(xiàn)娩脾。
priceSwapIn
:獲取換入幣種的價格,賣出幣種時候使用腋粥。
priceSwapOut
:獲取換出幣種的價格晦雨,買入幣種的時候使用架曹。
兩個函數(shù)結構相似隘冲,區(qū)分的是 base 和 quote 幣種要互換位置。買入quote數(shù)目->預計付出base數(shù)目->成交->實際付出和預計數(shù)量不一定一樣绑雄,賣出quote數(shù)目->預計獲得base數(shù)目->成交->實際獲得和預計數(shù)量不一定一樣
swapExactIn
:在這個函數(shù)里面會實際調用 eth 合約實現(xiàn)交易展辞,如果要賣出幣種調用的是這個函數(shù)
swapExactOut
:在這個函數(shù)里面會實際調用 eth 合約實現(xiàn)交易,如果要買入幣種調用的是這個函數(shù)
其實兩個函數(shù)并沒有什么本質區(qū)別站在不同的角度上 swapIn 也是一種 swapOut万牺,代碼也是一樣的只是變量交換了位置罗珍。
授權
授權幣種
141@ethereum.ts
const wallet = ethereumService.getWallet(req.body.privateKey);
// Getting token info
const tokenContractInfo = ethereumService.getERC20TokenAddress(
req.body.token
);
const tokenAddress = tokenContractInfo.address;
const gasPrice = req.body.gasPrice || ethereumGasService.getGasPrice();
let amount = ethers.constants.MaxUint256;
if (req.body.amount) {
amount = ethers.utils.parseUnits(
req.body.amount,
tokenContractInfo.decimals
);
}
// call approve function
let approval;
try {
approval = await ethereumService.approveERC20(
wallet,
spender,
tokenAddress,
amount,
gasPrice
);
} catch (err) {
approval = err;
}
......
175@ethereums.ts
const contract = new Contract(tokenAddress, abi.ERC20Abi, wallet);
contract.approve(spender, amount, {
gasPrice: gasPrice * 1e9,
// fixate gas limit to prevent overwriting
gasLimit: this.config.approvalGasLimit,
});
獲取錢包授權的數(shù)量
//88@ethereums.ts
const wallet = ethereumService.getWallet(req.body.privateKey);
approvals[symbol] = await ethereumService.getERC20Allowance(
wallet,
spender,
address,
decimals
);
const contract = new Contract(tokenAddress, abi.ERC20Abi, this.provider);
const allowance = await contract.allowance(wallet.address, spender);
獲取 swap 價格
//166@uniswap.js
//還有一個priceSwapIn 類似
async priceSwapOut(tokenIn, tokenOut, tokenOutAmount) {
await this.extend_update_pairs([tokenIn, tokenOut]);
const tOut = this.tokenList[tokenOut];
const tIn = this.tokenList[tokenIn];
const tokenAmountOut = new uni.TokenAmount(
tOut,
ethers.utils.parseUnits(tokenOutAmount, tOut.decimals)
);
//先查詢本地緩存
if (this.pairs.length === 0) {
const route = await this.fetch_route(tIn, tOut);
const trade = uni.Trade.exactOut(route, tokenAmountOut);
if (trade !== undefined) {
const expectedAmount = trade.maximumAmountIn(this.allowedSlippage);
this.cachedRoutes[tIn.symbol + tOut.Symbol] = trade;
return { trade, expectedAmount };
}
return;
}
// 最大可以換出的數(shù)目
let trade = uni.Trade.bestTradeExactOut(
this.pairs,
this.tokenList[tokenIn],
tokenAmountOut,
{ maxHops: 5 }
)[0];
if (trade === undefined) {
trade = this.cachedRoutes[tIn.symbol + tOut.Symbol];
} else {
this.cachedRoutes[tIn.symbol + tOut.Symbol] = trade;
}
const expectedAmount = trade.maximumAmountIn(this.allowedSlippage);
return { trade, expectedAmount };
}
SWAP
// 254@uniswap.js
//還有一個swapExactIn 類似
async swapExactOut(wallet, trade, tokenAddress, gasPrice) {
const result = uni.Router.swapCallParameters(trade, {
ttl: TTL,
recipient: wallet.address,
allowedSlippage: this.allowedSlippage,
});
const contract = new ethers.Contract(
this.router,
proxyArtifact.abi,
wallet
);
//真正調用合約的地方
const tx = await contract[result.methodName](...result.args, {
gasPrice: gasPrice * 1e9,
gasLimit: GAS_LIMIT,
value: result.value,
});
debug(`Tx Hash: ${tx.hash}`);
return tx;
}
交易記錄
//202@ethereum.
//json-rpc-provider
async perform(method: string, params: any): Promise<any> {
const args = this.prepareRequest(method, params);
if (args == null) {
logger.throwError(method + " not implemented", Logger.errors.NOT_IMPLEMENTED, { operation: method });
}
try {
return await this.send(args[0], args[1])
} catch (error) {
return checkError(method, error, params);
}
}
...
send(method: string, params: Array<any>): Promise<any> {
this.emit("debug", {
action: "request",
request: deepCopy(request),
provider: this
});
}
emit(eventName: EventType, ...args: Array<any>): boolean {
let result = false;
let stopped: Array<Event> = [ ];
let eventTag = getEventTag(eventName);
this._events = this._events.filter((event) => {
if (event.tag !== eventTag) { return true; }
setTimeout(() => {
event.listener.apply(this, args);
}, 0);
result = true;
if (event.once) {
stopped.push(event);
return false;
}
return true;
});
stopped.forEach((event) => { this._stopEvent(event); });
return result;
}
eth 錢包余額
//34@ethereum.ts
const wallet: ethers.Wallet = ethereumService.getWallet(
req.body.privateKey || ''
);
for (const symbol of JSON.parse(req.body.tokenList)) {
const tokenContractInfo = ethereumService.getERC20TokenAddress(symbol);
if (!tokenContractInfo) {
continue;
}
tokenContractList[symbol] = tokenContractInfo;
}
// Getting user balancers
const balances: Record<string, string> = {};
balances.ETH = await ethereumService.getETHBalance(wallet);
await Promise.all(
Object.keys(tokenContractList).map(async (symbol) => {
if (tokenContractList[symbol] !== undefined) {
const address = tokenContractList[symbol].address;
const decimals = tokenContractList[symbol].decimals;
balances[symbol] = await ethereumService.getERC20Balance(
wallet,
address,
decimals
);
} else {
logger.error(`Token contract info for ${symbol} not found`);
}
})
);