与Clipper集成

开发人员如何将 Clipper 交易构建到他们自己的 DeFi 应用程序中的指南。

Ethereum Mainnet

Clipper 从 0x 开始实现 PLP API,方便开发者将 Clipper 集成到自己的 DeFi 应用中。重要的是,Clipper 采用“发送,然后交换”模式。这意味着必须在同一交易中将资产发送到我们的池中,然后必须调用适当的交换函数。如果在调用交换用户资金之前在交易中发送资产,则可能会被盗。鼓励开发人员参考我们的 ClipperRouter 合约,查看如何与 Clipper 进行交易的示例,并加入我们的 Discord 寻求帮助
从 PLP API 中,函数 getSellQuote、sellTokenForToken 和 sellTokenForETH 应该在我们的交换接口合约上调用。为了最大限度地减少 gas,请在 Pool 合约上调用 sellETHForToken,将 ETH 作为值附加到函数调用中。在 getSellQuote 中,使用地址 0x0000000000000000000000000000000000000000 或 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE 来表示 ETH。建议开发人员通过将合约调用中的auxiliaryBytes 设置为唯一字符串来识别交易来源,因为这些字节是由 Clipper 交换在交换事件中发出的。例如,我们的 Clipper Router 合约使用 ClipperRouter 的编码版本。
要了解池中代币的地址,开发者可以调用 nTokens(返回池中代币的数量)和 tokenAt(将 0 到 nTokens()-1 之间的整数作为输入,并返回池中代币的地址)池合约上的 ERC20 代币合约)。请注意,ETH 在池中始终可用,不被视为“代币”。 常见问题解答中提供了 Clipper 合约地址。

Polygon (询价架构)

Clipper 是在其他链上使用 RFQ 架构实现的,这些链的出块时间比以太坊主网更快,从 Polygon 开始。 在 RFQ 架构中,您首先要求我们的链下服务器报价交易价格,指定买卖代币以及目标输入或输出数量。 然后,您有很短的时间来接受该报价,并从我们的链下服务器接收签名证书。 然后,您必须将该交易和签名证书传递给我们的链上智能合约以执行交换。 与 Clipper 的主网实现非常相似,该基础设施实现了“发送,然后交换”模式,并旨在尽可能简单地在更大的交易集中进行链接。

获取交易所地址和资产信息

GET 到 https://api.clipper.exchange/rfq/pool?chain_id=.... 将 chain_id 设置为适当的(整数)链 ID。 Polygon 是 137。JSON 响应将包括池的地址和该链上的合约地址列表。 示例响应:
{
"pool": {
"chain_id": 137,
"address": "0xD01e3549160c62Acabc4D0EB89F67aAFA3de8EEd",
"num_assets": 7,
"pool_tokens": 1744171922596734892507136,
"value_in_usd": 1955850.4306469245
},
"assets": [{
"name": "USDC",
"address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"balance": "287945821795",
"price_in_usd": 0.9997835373,
"value_in_usd": 287883.49226496054,
"target_value_in_usd": 288882.55208657164
}, {
"name": "ETH",
"address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
"balance": "91082274931865214483",
"price_in_usd": 4559.0177414797,
"value_in_usd": 415245.7073487052,
"target_value_in_usd": 411368.75417127804
}, {
"name": "USDT",
"address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
"balance": "204708040486",
"price_in_usd": 1.0008107141,
"value_in_usd": 204874.0001808054,
"target_value_in_usd": 205684.37708563902
}, {
"name": "MATIC",
"address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
"balance": "255804849489186177066089",
"price_in_usd": 2.0681423682,
"value_in_usd": 529040.8472196101,
"target_value_in_usd": 514210.9427140975
}, {
"name": "DAI",
"address": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
"balance": "202296673572783491062883",
"price_in_usd": 1.0024245729,
"value_in_usd": 202787.15660528824,
"target_value_in_usd": 205684.37708563902
}, {
"name": "WBTC",
"address": "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6",
"balance": "450316030",
"price_in_usd": 62901.8231205992,
"value_in_usd": 283256.9926743044,
"target_value_in_usd": 288882.55208657164
}, {
"name": "GYEN",
"address": "0x482bc619eE7662759CDc0685B4E78f464Da39C73",
"balance": "3735473968223",
"price_in_usd": 0.00877056958018,
"value_in_usd": 32762.234353250915,
"target_value_in_usd": 41136.875417127805
}]
}
  • num_assets 将是一个整数值,其中包含池中的资产数量。
  • 顶层的 assets 键是描述池资产的长度为 num_assets 的列表。 每个资产的名称和地址是最重要的字段。

请求报价

使用以下键向 https://api.clipper.exchange/rfq/quote 端点发布 JSON 正文。
{
"output_asset_symbol": "ETH",
"input_asset_symbol": "USDT",
"chain_id": 137,
"output_amount": "1000000000000000000", // XOR: "input_amount": "1000000000000000000",
"time_in_seconds": 30
}
  • chain_id 应设置为适当的(整数)链 ID。 Polygon 是 137。
  • 应该指定 input_amount 或 output_amount 中的一个,并且该值应该是一个字符串
  • time_in_seconds 是指在区块链上接受报价和收到报价之间的时间量。 Polygon 的一个好建议是 30 秒。 随着这个值的增加,用户的报价会变得更糟。
  • input_asset_symbol 和 output_asset_symbol 应该对应于从上面的 /rfq/pool 调用返回的资产名称。 请注意,“ETH”和“MATIC”都是资产,但 Clipper 交易所使用这些资产的打包版本,通过在响应中包含合约地址来明确区分。
响应是一个 JSON 对象:
{
"id": "60aa826e-b4ee-4660-8b28-4ffb77e936f5",
"must_accept_by": "2021-10-25 22:39:45.408794+00:00",
"good_until": 1635201615,
"chain_id": 137,
"input_asset_address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
"input_amount": "3442034079",
"input_value_in_usd": 1.01,
"output_asset_address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
"output_amount": "1000000000000000000",
"output_value_in_usd": 1.00
}
must_accept_by 是一个人类可读的 UTC 时间戳,您必须通过该时间戳接受报价,并且预计持续时间很短。 如果这个时间已经过去,请求一个新的报价 - 在 must_accept_by 过去后服务器不会签署报价。

接受报价

使用以下 JSON 正文 POST 到 https://api.clipper.exchange/rfq/sign 端点:
{
"quote_id": "60aa826e-b4ee-4660-8b28-4ffb77e936f5",
"destination_address": "0x5901920A7b8cb1Bba39220FAC138Ffb3800dD212"
}
  • quote_id 应设置为上面请求的报价的 ID 字段
  • destination_address 将接收输出令牌。
响应是一个 JSON 对象,它将签名信息添加到报价单中:
{
"chain_id": 137,
"input_asset_address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
"output_asset_address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
"input_amount": "3442034079",
"output_amount": "1000000000000000000",
"good_until": 1635201615,
"destination_address": "0x5901920A7b8cb1Bba39220FAC138Ffb3800dD212",
"signature": {
"v": 28,
"r": "0x19e2400ad86ee2f353f7f7d7a859339e3ba9d056691872385ada9ddc1d104ea6",
"s": "0x5d8de6fe4e25b9c26817b0bc3c33a78ce1e24799aae3b89886f64ac6a98a3c37"
},
"clipper_exchange_address": "0xD01e3549160c62Acabc4D0EB89F67aAFA3de8EEd"
}

与Clipper交易所互动

根据您的用例,有几种不同的外部函数可用于与 Clipper 交换链上的交互。 在所有这些函数中,Signature 是一个由以下定义的结构:
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}

用代币交换代币

function swap(address inputToken, address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
function transmitAndSwap(address inputToken, address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
请注意,带符号的引号包含所有这些值,除了可以设置为任何字符串并在事件日志中用于识别目的的辅助数据。
swap 和 transferAndSwap 之间的区别在于,transmitAndSwap 首先将 inputToken 的 inputAmount 从 msg.sender 转移到交易所,而 swap 假设已经传输了适当数量的输入令牌。 您应该使用哪个函数取决于您是否以及如何将 Clipper 交换连接到其他操作。

处理本国货币

在每条链上,本地货币都具有特殊的特权和能力,并且通常与 ERC20 不兼容。 例如,MATIC 是 Polygon 上的本地货币。 与以太坊主网上的 Clipper 实现不同,Clipper 的 RFQ 架构仅使用 ERC20 代币,这意味着这些原生货币必须在与 Clipper 交换之前进行打包,并且 Clipper 只能从交易所返回这些资产的打包版本。
但是,为了方便起见,我们提供了两组直接处理本地货币的函数。 对于这些交换,您必须获得与本地货币的包装版本之间的交易的签名,因为这将是实际交换的内容。
对于将代币兑换为本地货币(例如:DAI 到 Polygon 上的 MATIC),请使用:
function sellTokenForEth(address inputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
function transmitAndSellTokenForEth(address inputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
与令牌到令牌交换一样,这些函数之间的区别在于,第一个函数假设至少 inputAmount 的 inputToken 已经发送,而第二个函数直接从 msg.sender 中提取该输入令牌。 进一步观察:
  • outputToken 参数不存在(因为它将是本地货币)
  • 这些函数将本地货币称为 Eth,而不管链
对于本地货币到代币的交换(例如:MATIC 到 Polygon 上的 USDC),请使用:
function sellEthForToken(address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external payable;
此函数可以通过将本地货币作为 msg.value 附加到调用中来操作,或者通过在函数调用之前将该本地货币转移到交易所合约来操作。