A connected market is a ReferenceCurve_v1 market whose collateral token is the issuance token of another House market, the base market. The periphery routers (AtomicBuyRouter_v1, AtomicSellRouter_v1, AtomicLoopRouter_v1 and AtomicRepayRouter_v1) trade on both markets in one transaction. The connected market’s reserve is a balance of the base market’s token, so its prices, fees and loans are all in that token. An address with the base market’s collateral token can buy, sell, loop or repay on the connected market in one router call, with no separate transaction to buy or sell the base market’s token.

Parts of a connected pair

The sell, loop and repay routers fix one base market, connected market and credit facility at deployment. The router code names in the table are the names of those fixed addresses in AtomicRouterBase_v1.

How a connected market works

Each market is an ordinary market as described in Markets and curves. The connection is only the choice of collateral token.
  • The connected market’s reserve is a balance of A.
  • Its reference price and every step price are in A per B.
  • Its buy fee and sell fee are taken in A and go to its fee treasury.
  • Its credit facility values locked B at the connected market’s reference price in A, and lends A from the connected market’s reserve (Credit and looping).
  • The connected market’s curve and credit facility read prices only from the connected market’s own segments.
  • A buy or sell on the connected market moves existing A between the trader and the connected market’s reserve. The base market’s supply and price do not change unless the route also trades on the base market.
A route from C to B is two buys, each with its own fee: RA(sA+nA)−RA(sA)=D−feeA,RB(sB+nB)−RB(sB)=nA−feeBR_A(s_A + n_A) - R_A(s_A) = D - \text{fee}_A, \qquad R_B(s_B + n_B) - R_B(s_B) = n_A - \text{fee}_B where:
  • DD is the deposit, in C
  • feeA=D⋅fA/10 000\text{fee}_A = D \cdot f_A / 10\,000, with fAf_A the base market’s buy fee in basis points
  • nAn_A is the A minted by the base market, which becomes the deposit on the connected market
  • feeB=nA⋅fB/10 000\text{fee}_B = n_A \cdot f_B / 10\,000, with fBf_B the connected market’s buy fee in basis points
  • nBn_B is the B minted by the connected market
  • RAR_A, RBR_B are each market’s cost of supply, and sAs_A, sBs_B each market’s supply before the route (Cost of supply)
Example. A buyer sends 1,000 C through AtomicBuyRouter_v1 with a minimum of 890 A from the first buy and 420 B from the second.
  • The base market’s buy fee is 100 bps (1%), and the buy falls within one step priced at 1.10 C per A.
  • The connected market’s buy fee is 100 bps (1%). Its supply sits in a step priced at 2.00 A per B with 300 tokens left, and the next step is priced at 2.40.
After the route:
  • the base market’s reserve holds 990.00 more C, and A’s supply is 900.00 higher
  • the connected market’s reserve holds 891.00 more A, and B’s supply is 421.25 higher
  • the base market’s fee treasury holds 10.00 C, and the connected market’s fee treasury holds 9.00 A
  • the router holds nothing from the call, so there is no refund

Routers

Shared checks

Errors from these checks are prefixed Module__AtomicRouterBase__. Errors specific to the repay router are prefixed Module__AtomicRepayRouter__. The sell, loop and repay routers inherit AtomicRouterBase_v1. At deployment it requires that:
  • the base market, connected market and credit facility are contracts
  • C, A and B are three different contracts
  • the connected market’s collateral token is A, and the credit facility belongs to the connected market
On every call:
  • The receiver cannot be the zero address, the router, either market or the facility.
  • The input amount must be above 0.
  • The call reverts if the deadline is before the current block time. A call at the deadline runs.
  • Each executed leg needs a minimum output above 0.
  • The router records its own balances of C, A and B before pulling input. After each leg, a balance below that record reverts the call. At the end, every balance must equal the record, or the call reverts.
  • A token pull that delivers a different amount from the one requested reverts.
  • Each leg approves the market for the exact amount and clears the approval after the leg.
AtomicBuyRouter_v1 does not inherit the base. It runs its own version of these checks, described below.

AtomicBuyRouter_v1

buyExactInput(firstMarket, secondMarket, amountIn, minIntermediateOut, minFinalOut, receiver, deadline)
  1. Checks the inputs. Both markets must be contracts and different from each other. The receiver cannot be the zero address or the router.
  2. Reads the tokens. The second market’s collateral token must equal the first market’s issuance token. The first market’s collateral token must differ from its issuance token.
  3. Pulls amountIn of the first market’s collateral token from the caller.
  4. Calls buyFor on the first market, with the router as receiver and minIntermediateOut as the minimum.
  5. Calls buyFor on the second market with all the tokens the first buy minted, with receiver as receiver and minFinalOut as the minimum.
  6. Sends any unspent collateral token and any unspent intermediate token back to the caller. Unspent amounts come from a buy that reaches the end of a curve.
Reverts (errors prefixed Module__AtomicBuyRouter__): InvalidMarket, InvalidAddress, InvalidAmount, InvalidMinimumOutput, ExpiredDeadline, InvalidToken, IncompatibleMarkets, UnsupportedTokenAlias, UnsupportedTokenBehavior (the pull delivered a different amount), UnexpectedBalanceChange, InsufficientIntermediateOutput, InsufficientFinalOutput, plus any revert from either market’s buy.

AtomicSellRouter_v1

sellExactInput(amountIn, minIntermediateOut, minFinalOut, receiver, deadline)
  1. Pulls amountIn of B from the caller.
  2. Calls sellTo on the connected market, with the router as receiver and minIntermediateOut as the minimum. The router receives A.
  3. Calls sellTo on the base market with all that A, with receiver as receiver and minFinalOut as the minimum.
  4. Requires that no A or C is left on the router, and emits Sold.
Reverts: the shared checks, InsufficientOutput, UnexpectedResidue (A or C left on the router), plus any revert from either market’s sell.

AtomicLoopRouter_v1

loopExactInput(params)
  1. Pulls amountIn of C from the caller.
  2. Calls buyFor on the base market, with the router as receiver.
  3. Calls buyAndBorrowFor on the credit facility with all that A. The new loans belong to receiver. At the end of the loop, the facility returns the A it still holds to the router (Looping).
  4. Emits Looped with each new loan’s ID, loan amount and locked tokens.
  5. Exits the A left over. ToCollateral calls sellTo on the base market and pays C to receiver. Reinvest calls buyFor on the connected market and mints B to receiver; this B is not locked.
  6. Settles. C left on the router goes to the caller. A left on the router goes to receiver if it is at or below maxKnxFallback.
Reverts: the shared checks, InvalidAmount (loops is 0), ExitClosed (the exit’s direction is closed, checked even when no A is left over), InvalidMinimumOutput, InsufficientOutput, FallbackExceeded (A left over above maxKnxFallback), UnexpectedResidue (B left on the router), plus any revert from either market or the facility.

AtomicRepayRouter_v1

repayExactInput(params)
  1. Pulls amountIn of C from the caller.
  2. Calls buyFor on the base market, with the router as receiver.
  3. Reads the loan. It must be active. With closeAndExit set, the caller must be the loan’s borrower.
  4. Calls repayFor on the credit facility with all that A. The facility repays at most the loan amount and sends the released B to the loan’s borrower, not to the caller.
  5. With closeAndExit set, requires that the loan is closed with a loan amount of 0.
  6. Sells the A not used for the repayment on the base market and pays C to the caller.
  7. With closeAndExit set, pulls the released B from the caller, sells it on the connected market for A, and sells that A on the base market for C, paid to the caller.
  8. Settles. C left on the router goes to the caller. A left on the router goes to the caller if it is at or below maxKnxFallback. Emits Repaid.
Reverts: the shared checks, InvalidLoan, CallerNotOwner, LoanNotClosed, ExitClosed (selling on the base market is closed, checked even when no A is left), InvalidMinimumOutput, InsufficientOutput, FallbackExceeded, UnexpectedResidue, plus any revert from either market or the facility, including repayFor reverting for a loan held by the early-access escrow.

Parameters

The routers have no settable parameters. The sell, loop and repay routers fix their market set in the constructor, and it cannot be changed. Each connected market’s own parameters are set as for any market (Markets and curves, Credit and looping).

Permissions the routers need

The routers have no access check of their own. Any address can call them. Each market sees the router as the caller, so the router address needs the permission for every market function it calls. The launch route leaves buyFor, sellTo, buyAndBorrowFor and repayFor ungranted. DeployPeripheryRouters_v1 deploys the sell, loop and repay routers for one market set, grants no permissions, and reverts unless each router already holds the permissions in the table. A grant to a router opens that function to every address that calls the router. Authority lists every role.

Edge cases

  • Each leg is an ordinary trade. Each market’s trading switches, fees, endowment gates and rounding apply to its leg (Markets and curves). A closed switch on either market reverts a route that trades on it.
  • Buy past capacity. When a buy reaches the end of a curve, the market refunds the unspent deposit to the router. AtomicBuyRouter_v1 returns it to the caller; the other routers return refunded C to the caller at settlement.
  • Zero-output exit. In the loop and repay routers, an exit trade whose quote rounds to zero is skipped when the A left over is at or below maxKnxFallback. That A goes to the receiver at settlement.
  • Released B on repayment. A repayment through AtomicRepayRouter_v1 sends released B to the loan’s borrower, whoever pays.
  • Whole-route revert. Any revert in any leg reverts the whole call, including the input pull.

Code

Source: house-markets/contracts at 0ebbc89a (branch integration/v12-post-qa).