Skip to main content

Each Transmuter takes one synthetic debt asset (alAsset) and is associated with one AlchemistV3 instance used for redemptions.

Variables​

StakingPosition​

A struct that describes a tranche of deposited synthetic debt assets which have an eventual claim to underlying.

amount
startBlock
maturationBlock

TransmuterInitializationParams​

State set by params at creation-time of the transmuter to initially configure it.

syntheticToken
timeToTransmute
transmutationFee
exitFee
feeReceiver
graphSize
  • Description - Present in the TransmuterInitializationParams struct but not read by the constructor. It has no effect on the deployed contract.
  • Type - uint256
  • Used By - none
  • Updated By - none
  • Read By - none

Constants​

Constant variables used as helpers or for informational purposes.

BPS
FIXED_POINT_SCALAR
  • Description - A multiplier that is used to be able to do fixed point math, since solidity does not natively handle decimals. Like ERC20 tokens which typically use 18 decimals, it expresses 1 as 1e18. Anything less is a fraction of 1.
  • Type - uint256
  • Used By
  • Updated By - NONE - constant
BLOCK_SCALING_FACTOR
version
  • Description - Constant expressing Alchemix version. Not used for anything in the contract.
  • Type - string
  • Updated By - NONE - constant
  • Read By - version()

Public State​

State that is available and can be read from outside of the contract.

Uint256​

depositCap
totalLocked
totalActiveLocked

Addresses​

pendingAdmin
alchemist
admin
  • Description - The current admin address. Set to msg.sender on deployment. Only this address can call functions guarded by the onlyAdmin modifier.
  • Type - address
  • Used By
  • Updated By
  • Read By - admin()
  • Notified By - AdminUpdated(address admin)

Private State​

Internal state of the contract. In most cases cannot be read from outside of the contract. In some cases certain aspects can be read using specific functions. (See ReadingState)

_positions
_stakingGraph
_nonce
_countsTowardCap

Functions​

Constructor​

Runs once at deployment. The deployer becomes the admin.

constructor(TransmuterInitializationParams params)
  • Description - Deploys the Transmuter and seeds its configuration from the TransmuterInitializationParams struct.

    Sets syntheticToken, timeToTransmute, transmutationFee, exitFee, and protocolFeeReceiver (from params.feeReceiver), then sets admin to msg.sender. The graphSize field of the struct is ignored. depositCap is left at its default of 0, so createRedemption reverts with DepositCapReached() until the admin calls setDepositCap. alchemist is also unset at deployment and is configured through setAlchemist.

    The contract inherits OpenZeppelin ERC721Enumerable and sets the ERC721 name to "Alchemix V3 Transmuter" and the symbol to "TRNSMTR". Position NFTs therefore expose the standard ERC721 functions (balanceOf, ownerOf, transferFrom, approve, and so on) plus the enumerable extension (totalSupply, tokenByIndex, tokenOfOwnerByIndex) in addition to the functions documented on this page.

    • @param params - the TransmuterInitializationParams struct described in the Variables section
  • Visibility Specifier - none (constructor)
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits - none
  • Reverts
    • IllegalArgument() - if params.feeReceiver is the zero address
    • IllegalArgument() - if params.timeToTransmute is 0
    • IllegalArgument() - if params.timeToTransmute exceeds type(int256).max
    • IllegalArgument() - if params.transmutationFee exceeds BPS (> 100%)
    • IllegalArgument() - if params.exitFee exceeds BPS (> 100%)

User Actions​

Functions that can be called by external accounts which influence the state or balance of the Transmuter.

createRedemption(uint256 syntheticDepositAmount, address recipient)
  • Description - Creates a time-locked redemption staked position that linearly vests over timeToTransmute blocks.

    Validates the deposit amount and capacity against both depositCap (using totalActiveLocked) and alchemist.totalSyntheticsIssued() (using totalLocked). Transfers the syntheticDepositAmount of alAsset from the function caller. Records a new StakingPosition from block.number to block.number + timeToTransmute. Updates the staking graph with the per-block redemption rate contributed by this position, adding it to the existing global rate so that redemptions for all positions can be served correctly. Mints a new position token to recipient, increments both totalLocked and totalActiveLocked, and marks the position as counting toward the deposit cap.

    • @param syntheticDepositAmount - amount of syntheticToken deposited in this staking position, to be locked until transmutation is finished or early exit is executed.
    • @param recipient - the address that will own the minted redemption position NFT.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • DepositZeroAmount() - if syntheticDepositAmount == 0
    • IllegalArgument() - if recipient is the zero address
    • DepositCapReached() - if totalActiveLocked + syntheticDepositAmount exceeds depositCap, or if totalLocked + syntheticDepositAmount exceeds alchemist.totalSyntheticsIssued(). The latter ensures that more deposits are not accepted than there is debt to service them.
claimRedemption(uint256 id)
  • Description - Settles and closes the redemption for the staked position identified by id, paying out the vested portion in yield tokens and returning the unvested synthetics minus the exit fee. If the transmuter cannot deliver enough yield tokens to cover the vested portion, the uncovered debt is refunded as synthetics as well.

    Validates the position exists and is not being claimed in its creation block. Computes the vested vs. unvested split using block-based linear vesting. Verifies ownership and burns the position token.

    Checks for bad debt in the Alchemist. badDebtRatio = mulDivUp(alchemist.totalSyntheticsIssued() * 10 ** underlyingDecimals, alchemist.getTotalLockedUnderlyingValue() + alchemist.convertYieldTokensToUnderlying(transmuter MYT balance)), rounded up so the ratio is never understated. Only when this ratio exceeds 1e18 is the vested amount scaled down: scaledTransmuted = amountTransmuted * 1e18 / badDebtRatio. First uses yield tokens already held from prior repayments, then redeems the remainder from the Alchemist (calls alchemist.redeem()). The amount actually paid out, distributable, is the smaller of the yield value of scaledTransmuted and the yield tokens available (held balance plus redeemedShares). The transmutation fee is taken from distributable only, rounded down, and the claimant receives the remainder. If distributable is worth less than scaledTransmuted in debt terms, the shortfall is refunded to the claimant in synthetics on top of the unvested amount: syntheticReturned = (amountNottransmuted - syntheticFee) + shortfallDebt. The exit fee applies to the unvested portion only, so no exit fee is charged on a shortfall refund. Only position.amount - (syntheticReturned + syntheticFee) is burned.

    If the position was not fully vested, the staking graph is updated to remove the remaining per-block rate. Transfers yield and synthetic payouts/fees, burns the transmuted synthetics, reduces totalSyntheticsIssued by the burned amount (calls alchemist.reduceSyntheticsIssued()), decrements totalLocked, removes the position from totalActiveLocked if it still counts toward the cap, and deletes the position. Informs the alchemist of its yieldToken quantity (calls alchemist.setTransmuterTokenBalance()).

    • @param id - the id of the staked position to claim and close
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - (uint256 claimYield, uint256 feeYield, uint256 syntheticReturned, uint256 syntheticFee) - the amount of yield tokens sent to the claimant, the yield fee sent to the protocol, the amount of synthetic tokens returned to the claimant, and the synthetic exit fee sent to the protocol.
  • Emits
  • Reverts
    • PositionNotFound() - if no position exists for id
    • PrematureClaim() - if claiming in the same block the position was opened
    • CallerNotOwner() - if the message sender does not own the position
acceptAdmin()
  • Description - Can only be called by the current pendingAdmin. Used to accept the admin role.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • IllegalState() - if pendingAdmin is the zero address (no pending transfer)
    • Unauthorized() - if msg.sender is not the current pendingAdmin
pokeMatured(uint256 id)
  • Description - Removes a fully matured position from the totalActiveLocked count, freeing up deposit cap space without requiring the position owner to claim. Can be called by anyone.

    Checks that the position exists, is fully matured (current block >= maturation block), and hasn't already been poked. Sets _countsTowardCap[id] to false and decrements totalActiveLocked by the position's amount.
    • @param id - the id of the matured position to poke
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • PositionNotFound() - if no position exists for id
    • PositionNotMatured(uint256 id, uint256 maturationBlock, uint256 currentBlock) - if the position has not yet fully matured
    • PositionAlreadyPoked(uint256 id) - if the position has already been poked

Admin Actions​

Functions guarded by the onlyAdmin modifier.

setPendingAdmin(address value)
  • Description - Sets the pending admin. First part of a two-step process to change the admin. The second step is the pendingAdmin accepting the role by calling acceptAdmin.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • IllegalArgument() - if msg.sender is not the current admin
setAlchemist(address value)
  • Description - Sets the address of the AlchemistV3 contract that will be used to redeem against for this Transmuter.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • IllegalArgument() - if msg.sender is not the current admin
setDepositCap(uint256 cap)
  • Description - Sets the deposit cap variable, or maximum amount of synthetics that can be staked in the transmuter at any one time.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • IllegalArgument() - if msg.sender is not the current admin
    • IllegalArgument() - if cap exceeds type(int256).max
setTransmutationFee(uint256 fee)
  • Description - Sets the fee as a percentage in BPS for transmutation.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • IllegalArgument() - if msg.sender is not the current admin
    • IllegalArgument() - if fee exceeds BPS (> 100%)
setExitFee(uint256 fee)
  • Description - Sets the fee as a percentage in BPS for early exits from positions staked for transmutation.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • IllegalArgument() - if msg.sender is not the current admin
    • IllegalArgument() - if fee exceeds BPS (> 100%)
setTransmutationTime(uint256 time)
  • Description - Sets the duration of time it takes for new staked positions of synthetic assets to be transmuted in blocks.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • IllegalArgument() - if msg.sender is not the current admin
    • IllegalArgument() - if time is 0
    • IllegalArgument() - if time exceeds type(int256).max
setProtocolFeeReceiver(address value)
  • Description - Sets the address that will receive fees from the Transmuter.
  • Visibility Specifier - external
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits
  • Reverts
    • IllegalArgument() - if msg.sender is not the current admin
    • IllegalArgument() - if value is the zero address

Internal Operations​

Functions that are cannot be called by other contracts or EOAs. Used only as helpers for performing internal logic.

_updateStakingGraph(int256 amount, uint256 blocks)
  • Description - Updates the staking graph with a new redemption schedule starting at the current block.

    Delegates to _stakingGraph.addStake(amount, block.number, blocks). Positive amount values add per-block redemption weight, negative values remove it. The change applies for the amount of passed in blocks.

    • @param amount - scaled redemption amount to apply (scaled by BLOCK_SCALING_FACTOR)
    • @param blocks - number of blocks over which the amount should apply
  • Visibility Specifier - private
  • State Mutability Specifier - nonpayable
  • Returns - none
  • Emits - none
  • Reverts - none
_checkArgument(bool expression)
  • Description - Validates a boolean condition intended to check arguments.
    • @param expression - the boolean expression to validate
  • Visibility Specifier - internal
  • State Mutability Specifier - pure
  • Returns - none
  • Emits - none
  • Reverts - IllegalArgument() - if the expression evaluates to false
_checkState(bool expression)
  • Description - Validates a boolean condition intended to check state.
    • @param expression - the boolean expression to validate
  • Visibility Specifier - internal
  • State Mutability Specifier - pure
  • Returns - none
  • Emits - none
  • Reverts - IllegalState() if the expression evaluates to false

Reading State​

Reads derived, calculated, or internal state. For getters of public variables see the Variable section.

tokenURI(uint256 id)
  • Description - Gets the token URI for a Transmuter NFT token with a specfic id.
  • Visibility Specifier - public
  • State Mutability Specifier - view
  • Returns - string memory - the URI of the Transmuter NFT with the passed ID
  • Emits - none
  • Reverts - none
getPosition(uint256 id)
  • Description - Gets the StakingPosition identified by id.
  • Visibility Specifier - external
  • State Mutability Specifier - view
  • Returns - StakingPosition memory
  • Emits - none
  • Reverts - none
queryGraph(uint256 startBlock, uint256 endBlock)
  • Description - Allows callers to see how much is scheduled to be redeemed between two blocks numbers.

    Queries the staking graph to calculate the total redemption amount that is scheduled to be applied for all staked positions between two block numbers by delegating to _stakingGraph.queryStake(startBlock, endBlock). Returns 0 if endBlock is less than startBlock, or if the query does not find an amount for that block span. Otherwise, it uses FixedPointMath.mulDivUp to scale the queried amount back down by BLOCK_SCALING_FACTOR to restore to token units and returns the result.

    • @param startBlock - block number at which to start the query range
    • @param endBlock - block number at which to end the query range
  • Visibility Specifier - external
  • State Mutability Specifier - view
  • Returns - uint256 scheduledAmountToBeRedeemed - total expected redemption amount of scheduled redemptions between the start and end blocks in token units.
  • Emits - none
  • Reverts - none

Events​

  • AdminUpdated(address admin) - Emitted when the admin address is updated.
  • PendingAdminUpdated(address pendingAdmin) - Emitted when the pending admin is updated.
  • AlchemistUpdated(address alchemist) - Emitted when the associated alchemist is updated.
  • PositionCreated(address indexed creator, uint256 amountStaked, uint256 nftId) - Emitted when a position is created. creator is msg.sender, which may differ from the recipient that owns the minted NFT.
  • PositionClaimed(address indexed claimer, uint256 amountClaimed, uint256 amountUnclaimed) - Emitted when a position is claimed.
  • GraphSizeUpdated(uint256 size) - Defined in the interface but not currently emitted by any function in the contract.
  • DepositCapUpdated(uint256 cap) - Emitted when the deposit cap is updated.
  • TransmutationTimeUpdated(uint256 time) - Emitted when the transmutation time is updated.
  • TransmutationFeeUpdated(uint256 fee) - Emitted when the transmutation fee is updated.
  • ExitFeeUpdated(uint256 fee) - Emitted when the early exit fee is updated.
  • ProtocolFeeReceiverUpdated(address receiver) - Emitted when the fee receiver is updated.
  • PositionPoked(uint256 indexed id, uint256 amountRemovedFromCap) - Emitted when a fully matured position is poked, removing its amount from the active deposit cap count.

Errors​

  • DepositZeroAmount() - Reverts when attempting to create a redemption with a zero deposit amount.
  • DepositCapReached() - Reverts when a deposit would push totalActiveLocked above depositCap, or totalLocked above alchemist.totalSyntheticsIssued().
  • PositionNotFound() - Reverts when referencing a position ID that does not exist.
  • PrematureClaim() - Reverts when attempting to claim a position in the same block it was created.
  • CallerNotOwner() - Reverts when the caller does not own the position NFT they are trying to claim.
  • PositionNotMatured(uint256 id, uint256 maturationBlock, uint256 currentBlock) - Reverts when attempting to poke a position that has not yet fully matured.
  • PositionAlreadyPoked(uint256 id) - Reverts when attempting to poke a position that has already been poked.
  • NotRegisteredAlchemist() - Defined in TransmuterErrors.sol but not currently used by any function in the contract.
  • AlchemistDuplicateEntry() - Defined in TransmuterErrors.sol but not currently used by any function in the contract.
  • DepositTooLarge() - Defined in TransmuterErrors.sol but not currently used by any function in the contract.
  • IllegalArgument() - Reverts when a function argument fails a validation check via _checkArgument(). Used across admin setter functions.
  • IllegalState() - Reverts when a state precondition fails via _checkState(). Used in acceptAdmin() to verify a pending admin exists.
  • Unauthorized() - Reverts when msg.sender is not authorized. Used in acceptAdmin() when the caller is not the pendingAdmin.