Last Look
AirSwap Last Look (LL) is a protocol used by market makers to stream quotes to takers. Takers periodically send signed orders to the maker, which then has the “last look” and option to fill it.
Protocol Features
    Takers can see quotes without connecting a wallet, and quotes update in realtime
    Takers don't submit or spend gas on transactions, the maker does instead
    Better prices from makers since their prices are not locked into an expiry
There is a possibility the maker declines the order because the market has moved, but they're generally amenable to small fluctuations to maintain a good trading relationship with the taker.
Protocol Summary
Last Look is only available over WebSocket. In Last Look, clients are signers and servers are senders.
    1.
    Client connects to server via WebSocket and subscribes to pricing.
    2.
    Server streams pricing to client, which in turn sends orders to the server using the pricing.
    3.
    Server may send the order to Ethereum for settlement.
For information on finding counter-parties, see Discovery.

Methods

initialize

To support Last Look, the server must call initialize upon connection by the client and indicate last-look among its list of supported protocols. Additional params may be included for the swapContract the server intends to use, the senderWallet the server intends to use, and optionally a senderServer if the server is not receiving consider calls over the socket and instead an alternative JSON-RPC over HTTP endpoint. The initialize method either returns true or throws an error if something went wrong on the client side.
1
initialize([
2
{
3
name: "last-look",
4
version: "1.0.0",
5
params: {
6
swapContract: string,
7
senderWallet: string,
8
senderServer?: string,
9
}
10
}, ...
11
]): boolean
Copied!

subscribe

Client subscribes to pricing updates for a list of token pairs. Returns current formula or levels for each pair.
1
subscribe([
2
{
3
baseToken: string,
4
quoteToken: string
5
}, { ... }
6
]): [
7
{
8
baseToken: string,
9
quoteToken: string,
10
minimum: string,
11
bid: Levels | Formula,
12
ask: Levels | Formula
13
}, { ... }
14
]
Copied!
Client may also subscribe to pricing updates for all available pairs.
1
subscribeAll(): [
2
{
3
baseToken: string,
4
quoteToken: string,
5
minimum: string,
6
bid: Levels | Formula,
7
ask: Levels | Formula
8
}, { ... }
9
]
Copied!

unsubscribe

Client unsubscribes from pricing updates for a list of token pairs. Returns a boolean.
1
unsubscribe([
2
{
3
baseToken: string,
4
quoteToken: string
5
}, { ... }
6
]): boolean
Copied!
Client may also unsubscribe from all subscriptions.
1
unsubscribeAll(): boolean
Copied!

updatePricing

Server updates pricing for one or more token pairs. Returns boolean true if accepted by the client.
1
updatePricing([
2
{
3
baseToken: string,
4
quoteToken: string,
5
minimum: string,
6
bid: Levels | Formula,
7
ask: Levels | Formula
8
}, { ... }
9
]): boolean
Copied!

consider

Client provides a priced order to the server. If the server has set a senderServer this method is to be called on that URL via JSON-RPC over HTTP. Returns boolean true if accepted by the server.
1
consider({
2
nonce: string,
3
expiry: string,
4
signerWallet: string,
5
signerToken: string,
6
signerAmount: string,
7
senderToken: string,
8
senderAmount: string,
9
v: string,
10
r: string,
11
s: string
12
}): boolean
Copied!

Pricing

Server pricing can be communicated either by levels or a formula. All input and output values for pricing are in base units rather than atomic units. When generating orders, all values must be converted to atomic units.

Levels

The server can specify levels to use for pricing. Each level is a tuple of amount and price at that level.
1
[
2
{
3
"baseToken": "0xdac17f958d2ee523a2206206994597c13d831ec7",
4
"quoteToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
5
"bid": [
6
["100", "0.00053"],
7
["1000", "0.00061"],
8
["10000", "0.0007"]
9
],
10
"ask": [
11
["100", "0.00055"],
12
["1000", "0.00067"],
13
["10000", "0.0008"]
14
]
15
},
16
{
17
"baseToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
18
"quoteToken": "0xdac17f958d2ee523a2206206994597c13d831ec7",
19
"bid": [
20
["0.5", "2000"],
21
["1", "2010"],
22
["10", "2050"]
23
],
24
"ask": [
25
["0.5", "2001"],
26
["1", "2015"],
27
["10", "2060"]
28
]
29
}
30
]
Copied!

Examples

Client wants to swap 1000 USDT into WETH. Client looks up baseToken USDT and quoteToken WETH and uses the bid levels above. The first 100 would be multiplied by 0.00053 and second 900 would be multiplied by 0.00061 for a total of 0.602 WETH.
Client wants to swap 1 WETH into USDT. Client looks up baseToken WETH and quoteToken USDT and uses the bid levels above. The first 0.5 would be multiplied by 2000 and second 0.5 would be multiplied by 2010 for a total of 2005 USDT.
Client wants to swap WETH into 1000 USDT. Client looks up baseToken USDT and quoteToken WETH and uses the ask levels above. The first 100 would be multiplied by 0.00055 and second 90 would be multiplied by 0.00067 for a total of 0.658 WETH.
Client wants to swap USDT into 1 WETH. Client looks up baseToken WETH and quoteToken USDT and uses the ask levels above. The first 0.5 would be multiplied by 2001 and second 90 would be multiplied by 2015 for a total WETH amount of 2008 USDT.

Formula

The server can specify formulas to use for pricing. Each formula is an expression with operations including addition, subtraction, multiplication, and division, where x is provided by the client.
1
[
2
{
3
"baseToken": "0xdac17f958d2ee523a2206206994597c13d831ec7",
4
"quoteToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
5
"bid": "x*0.00053",
6
"ask": "x*0.00055"
7
},
8
{
9
"baseToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
10
"quoteToken": "0xdac17f958d2ee523a2206206994597c13d831ec7",
11
"bid": "x*2000",
12
"ask": "x*2001"
13
}
14
]
Copied!

Examples

Client wants to swap 1000 USDT into WETH. Client looks up baseToken USDT and quoteToken WETH and uses the bid levels above. 1000 is multiplied by 0.00053 for a total of 0.53 WETH.
Client wants to swap 1 WETH into USDT. Client looks up baseToken WETH and quoteToken USDT and uses the bid levels above. 1 is multiplied by 2000 for a total of 2000 WETH.
Client wants to swap WETH into 1000 USDT. Client looks up baseToken USDT and quoteToken WETH and uses the ask levels above. 1000 is multiplied by 0.00055 for a total of 0.55 WETH.
Client wants to swap USDT into 1 WETH. Client looks up baseToken WETH and quoteToken USDT and uses the ask levels above. 1 is multiplied by 2001 for a total of 2001 WETH.

Protocol

To find counterparties, see Discovery. With server URLs in hand, clients connect to each and calls methods as JSON-RPC over WebSocket.
Upon connection, the server sends an initialize notification to the client.
1
{
2
"jsonrpc": "2.0",
3
"method": "initialize",
4
"id": "xyz",
5
"params": [
6
[
7
{
8
"name": "last-look",
9
"version": "1.0.0",
10
"params": {
11
"swapContract": "0xc549a5c701cb6e6cbc091007a80c089c49595468",
12
"senderWallet": "0x73BCEb1Cd57C711feaC4224D062b0F6ff338501f",
13
"senderServer": "www.maker.com"
14
}
15
}
16
]
17
]
18
}
Copied!
The client may then subscribe to pricing updates.
1
{
2
"jsonrpc": "2.0",
3
"method": "subscribeAll",
4
"id": "123",
5
"params": []
6
}
Copied!
The server then continuously updates the client with new pricing.
1
{
2
"jsonrpc": "2.0",
3
"method": "updatePricing",
4
"id": "qrs",
5
"params": [
6
[
7
{
8
"baseToken": "0xdac17f958d2ee523a2206206994597c13d831ec7",
9
"quoteToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
10
"bid": [
11
["100", "0.00053"],
12
["1000", "0.00061"],
13
["10000", "0.0007"]
14
],
15
"ask": [
16
["100", "0.00055"],
17
["1000", "0.00067"],
18
["10000", "0.0008"]
19
]
20
}
21
]
22
]
23
}
Copied!
The client may send an order to the server to consider a swap.
1
{
2
"jsonrpc": "2.0",
3
"id": "abc",
4
"method": "consider",
5
"params": {
6
"nonce": "1",
7
"expiry": "1629117312",
8
"signerWallet": "0x0...",
9
"signerToken": "0xdac17f958d2ee523a2206206994597c13d831ec7",
10
"signerAmount": "1000000000",
11
"senderToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
12
"senderAmount": "530000000000000000",
13
"v": "28",
14
"r": "0x0...",
15
"s": "0x0..."
16
}
17
}
Copied!
After the server accepts an order, parameters are submitted as an Ethereum transaction to the swap function on the Light contract, which emits a Swap event on success.
1
function swap(
2
uint256 nonce,
3
uint256 expiry,
4
address signerWallet,
5
IERC20 signerToken,
6
uint256 signerAmount,
7
IERC20 senderToken,
8
uint256 senderAmount,
9
uint8 v,
10
bytes32 r,
11
bytes32 s
12
) external;
Copied!
1
event Swap(
2
uint256 indexed nonce,
3
uint256 timestamp,
4
address indexed signerWallet,
5
IERC20 signerToken,
6
uint256 signerAmount,
7
uint256 signerFee,
8
address indexed senderWallet,
9
IERC20 senderToken,
10
uint256 senderAmount
11
);
Copied!
The client may subscribe to a filter for a Swap event with the nonce they provided to the server.
Last modified 24d ago