Skip to content

Reference

Contracts

Addresses, events, owner action kinds and the EIP-712 domain.

On this page

Mirror is three contracts, written from scratch in Solidity 0.8.30 with OpenZeppelin against Perpl's published Exchange ABI. No Perpl code is reused. Source: github.com/Jagadeeshftw/mirror (contracts/src), MIT licence.

Addresses (Monad mainnet, chain id 143)

Contract Address Notes
MirrorAccountFactory pending (not deployed yet) Deploys one EIP-1167 clone per (owner, salt)
MirrorAccount (implementation) pending (not deployed yet) Shared code of every follower account
KeeperRegistry pending (not deployed yet) Addresses allowed to call mirror()
Perpl Exchange 0x34B6552d57a35a1D042CcAe1951BD1C370112a6F Third party, live
AUSD (collateral) 0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a Third party, live, 6 decimals

Mirror's own contracts are not deployed on mainnet yet; their rows say "pending" until they are deployed and verified on MonadVision. The demo leader and demo follower accounts are listed on Team-run accounts once live.

MirrorAccountFactory

Deploys one MirrorAccount per (owner, salt) as a non-upgradeable EIP-1167 clone. Anyone may deploy an account for an owner; the owner is fixed at creation. The address is deterministic, so the app shows it before deployment.

Function Notes
createAccount(address owner, bytes32 salt) → MirrorAccount Deploys and initialises the clone.
predictAccount(address owner, bytes32 salt) → address Counterfactual address.
isAccount(address) → bool True for clones deployed by this factory.
event AccountCreated(address indexed owner, address indexed account, bytes32 salt);

KeeperRegistry

The set of addresses allowed to call MirrorAccount.mirror. Ownable2Step; the owner can add or remove keepers and has no access to any account.

event KeeperSet(address indexed keeper, bool allowed);

MirrorAccount

Keeper

Function Notes
mirror(MirrorOrder order) → bool executed Copy one leader fill. Returns false (and emits Blocked) when a rule blocks it.
struct MirrorOrder {
    uint32  leaderAccountId; // leader whose fill this copies
    uint32  perpId;
    uint8   orderType;       // 0 OpenLong, 1 OpenShort, 2 CloseLong, 3 CloseShort
    uint64  lotLNS;
    uint64  pricePNS;        // IOC limit price, within maxSlippageBps of mark
    uint16  leverageHdths;   // opening orders only
    uint16  maxMatches;      // capped at 1,000
    bytes32 leaderRef;       // leader fill reference, for attribution and latency
}

Deposits (anyone may relay; funds come from and belong to the owner)

Function Notes
depositWithPermit(amount, deadline, v, r, s) ERC-2612 permit signed by the owner.
depositWithAuthorization(amount, validAfter, validBefore, nonce, v, r, s) ERC-3009 receive authorization signed by the owner.
deposit(amount) Owner only, from a standing allowance.

Owner (direct, or relayed through execute)

Function Notes
setPolicy(Policy) See Policy reference.
follow(Policy, MirrorOrder[] matches) Set policy, unpause, match now.
matchNow(MirrorOrder[] matches) Opening orders only, up to 16.
setPaused(bool) Pause or resume opening copies.
closeAll(uint16 slippageBps) Pause and close every position.
withdraw(uint256 amount) Always pays the owner.
exchangeCall(bytes data) Escape hatch: any Perpl Exchange call as the account.
sweep(address token) Send a stray token balance to the owner.
execute(Action, bytes signature) Run a signed owner action. Anyone may call.

Views

owner(), perplAccountId(), paused(), netDeposits(), actionNonce(), leaders(), marketIds(), markets(perpId), equity(), targetLots(perpId, side), actionDigest(Action), DEPOSIT_CAP().

Events

Event Emitted when
Initialized(address indexed owner) The clone is initialised.
PerplAccountCreated(uint256 indexed perplAccountId) The first deposit opens the Perpl account.
Deposited(address indexed from, uint256 amount, uint256 netDeposits) A deposit lands.
Withdrawn(address indexed to, uint256 amount, uint256 netDeposits) A withdrawal is paid to the owner.
PolicyUpdated(maxLeverageHdths, maxSlippageBps, dailyLossBps, drawdownBps, expiry, leaders, markets) The policy is set.
PausedSet(bool paused) Paused or resumed.
Mirrored(keeper, leaderAccountId, perpId, orderType, lotLNS, pricePNS, leverageHdths, lotsBefore, lotsAfter, leaderRef) A copy (or match-now order) executed.
Blocked(keeper, leaderAccountId, perpId, reason, orderType, lotLNS, limit, actual, leaderRef) A rule blocked a copy.
Followed(uint256 matchOrders, uint256 matchesExecuted) A follow with match now completed.
ClosedAll(uint16 slippageBps, uint256 positionsClosed) Close all completed.
ExchangeCalled(bytes data, bytes result) The owner used the escape hatch.
Swept(address indexed token, uint256 amount) A token was swept to the owner.
ActionExecuted(uint8 indexed kind, uint256 nonce) A signed action ran.
RiskUpdated(uint32 day, uint128 dayStartEquity, uint128 highWaterEquity, uint256 equity) Loss-stop baselines were refreshed during a check.

Block reasons

BlockReason values, as emitted in Blocked.reason:

Value Name limit / actual
1 Paused 0 / 0
2 Expired expiry / block time
3 LeaderNotAllowed 0 / leader account id
4 LeaderSideMismatch copy side / leader side (max uint if flat)
5 MarketNotAllowed 0 / perpId
6 LeverageTooHigh max / requested (hundredths)
7 SlippageTooHigh price bound / limit price
8 FlipNotAllowed 0 / current lots
9 StaleMark 0 / 0
10 ExceedsLeaderTarget target lots / lots after
11 ExceedsMaxNotional cap / notional (AUSD, 6 decimals)
12 DailyLossStop equity floor / equity
13 DrawdownStop equity floor / equity
14 LeverageTooLow 100 / requested

Action kinds

Signed owner actions run through execute(Action, signature). data is the ABI encoding of the arguments.

Kind Name data
1 ACTION_SET_POLICY abi.encode(Policy)
2 ACTION_SET_PAUSED abi.encode(bool)
3 ACTION_CLOSE_ALL abi.encode(uint16 slippageBps)
4 ACTION_WITHDRAW abi.encode(uint256 amount)
5 ACTION_EXCHANGE_CALL abi.encode(bytes callData)
6 ACTION_SWEEP abi.encode(address token)
7 ACTION_FOLLOW abi.encode(Policy, MirrorOrder[])
8 ACTION_MATCH_NOW abi.encode(MirrorOrder[])

EIP-712 domain

Field Value
name Mirror Account
version 1
chainId 143
verifyingContract the follower's MirrorAccount (clone) address
Action(uint8 kind,bytes data,uint256 nonce,uint256 deadline)

The struct hash is keccak256(abi.encode(ACTION_TYPEHASH, kind, keccak256(data), nonce, deadline)). nonce must equal the account's current actionNonce() and increments on use; deadline is a unix timestamp after which the signature is rejected (ActionExpired). actionDigest(action) returns the digest, so clients can show exactly what is being signed.