Centrifuge
    • Options
    • Versions and GitHub Sync
    • Transfer ownership
    • Delete this note
    • Template
    • Save as template
    • Insert from template
    • Export
    • Dropbox
    • Google Drive
    • Import
    • Dropbox
    • Google Drive
    • Gist
    • Clipboard
    • Download
    • Markdown
    • HTML
    • Raw HTML
    • ODF (Beta)
    • PDF (Beta)
    • Sharing Link copied
    • /edit
    • View mode
      • Edit mode
      • View mode
      • Book mode
      • Slide mode
      Edit mode View mode Book mode Slide mode
    • Note Permission
    • Read
      • Owners
      • Signed-in users
      • Everyone
      Owners Signed-in users Everyone
    • Write
      • Owners
      • Signed-in users
      • Everyone
      Owners Signed-in users Everyone
    • More (Comment, Invitee)
    • Publishing
      Everyone on the web can find and read all notes of this public team.
      After the note is published, everyone on the web can find and read this note.
      See all published notes on profile page.
    • Commenting Enable
      Disabled Forbidden Owners Signed-in users Everyone
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Invitee
    • No invitee
Menu Sharing Help
Menu
Options
Versions and GitHub Sync Transfer ownership Delete this note
Export
Dropbox Google Drive
Import
Dropbox Google Drive Gist Clipboard
Download
Markdown HTML Raw HTML ODF (Beta) PDF (Beta)
Back
Sharing
Sharing Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
More (Comment, Invitee)
Publishing
Everyone on the web can find and read all notes of this public team.
After the note is published, everyone on the web can find and read this note.
See all published notes on profile page.
More (Comment, Invitee)
Commenting Enable
Disabled Forbidden Owners Signed-in users Everyone
Permission
Owners
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Invitee
No invitee
   owned this note    owned this note      
Published Linked with GitHub
Like BookmarkBookmarked
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# Liquidity Pools v2 Centrifuge Chain Cantina Contest ## Introduction Founded in 2017, Centrifuge is the institutional platform for credit onchain. Centrifuge was the first protocol where MakerDAO minted DAI against a real-world asset, the first onchain securitization, and Centrifuge launched the RWA Market with Aave. Centrifuge’s multi-chain strategy allows investors to access native RWA yields on the network of their choice. Centrifuge works based on a hub-and-spoke model. RWA pools are managed by borrowers on Centrifuge Chain, an application-specific blockchain built purposely for managing real world assets. Liquidity Pools are deployed on any other L1 or L2 where there is demand for RWA, and each Liquidity Pool deployment communicates directly with Centrifuge Chain using messaging layers. ## High level overview The architecture of Centrifuge Chain pallets is centered around `pallet-pool-system` which contains the core logic for managing investment pools such as bundling loans, slicing pools into tranchers and controlling investment epochs. In this particular document, we want to focus on the upper left corner which can be categorized as Liquidity Pools. ![](https://storage.googleapis.com/centrifuge-hackmd/upload_b215ca1c7ffd76cde2b8525c92a98fd4.png) ## Liquidity Pools The objective of Liquidity Pools is to facilitate investment activities on EVM chains using Solidity contracts ([repository](https://github.com/centrifuge/liquidity-pools)). Prior to the implementation of these pallets, investments could only be conducted directly on the Centrifuge Chain, either through local transactions or via XCM for any Polkadot chain with a bidirectional channel (UMP for relay chains, HRMP for parachains). Given the Polkadot ecosystem's liquidity constraints, this feature allows Centrifuge Chain to leverage the strengths of both ecosystems: Polkadot's efficiency, flexibility, and security, along with the liquidity available on EVM chains. The LP messages ([spec](https://centrifuge.hackmd.io/Soy4JiFbRSyovj53KW-a4A?view)) are exchanged between the EVM and Centrifuge Chains through the LP Router (or *Adapter* in Solidity terminoligy). As of now, our implementation exclusively utilizes Axelar EVM. Nevertheless, the architecture is designed to be adaptable, accommodating the integration of multiple gateways in the future. The Centrifuge Chain acts as a single source of truth. Thus, any necessary information from the Centrifuge Chain must be communicated to the corresponding EVM chain following the deployment of the Solidity contracts. For instance, when a new pool `P` is introduced on an EVM chain `E`, a series of LP messages must be dispatched from the Centrifuge Chain to `E` to establish the initial state before investments in `P` can commence on `E`. Another crucial principle is that assets invested on EVM chains are not directly transferred. Instead, they are locked within the contract and represented on the Centrifuge Chain as a local entity `CurrencyId::ForeignAsset(u64)`. ## Routing Here we define all entities required for sending/receiving message ```plantuml @startuml skinparam roundcorner 10 cloud "legend" { component "I'm a pallet" as a rectangle "I'm an interface" as b rectangle #Application "I'm a type configured\n at runtime level" as c a -[hidden]down- b b -[hidden]down- c } skinparam component { BackgroundColor #Motivation } skinparam rectangle { BackgroundColor #Strategy } [pallet-liquidity-pools-gateway] as gateway [pallet-liquidity-pools] as lp [pallet-liquidity-pools-queue] as events [pallet-liquidity-pools-forwarder] as forwarder frame " routers "{ port "n" as l [pallet-axelar-router] as axelar_evm [pallet-snowbridge-router \n <<unimplemented>>] as snowbridge } rectangle RouterDispatcher as dispatcher #Application rectangle MessageSerializer as serializer #Application rectangle MessageQueue rectangle MessageProcessor rectangle OutboundMessageHandler rectangle InboundMessageHandler rectangle MessageSender rectangle MessageReceiver lp .down.|> InboundMessageHandler : implements lp -down-> OutboundMessageHandler : uses gateway .up..|> OutboundMessageHandler : implements gateway -up--> InboundMessageHandler : uses gateway .right.|> MessageProcessor : implements gateway --> MessageQueue : uses events ..|> MessageQueue : implements events -up-> MessageProcessor : uses gateway .left.|> MessageReceiver : implements gateway --> MessageSender : uses forwarder .up.> MessageSender : implements & uses forwarder .up.> MessageReceiver : implements & uses dispatcher .up.|> MessageSender : implements dispatcher ---> axelar_evm : uses dispatcher ---> snowbridge : uses serializer .down.> MessageSender : implements & uses serializer .left.> MessageReceiver : implements & uses l .up..|> MessageSender : implements l -up--> MessageReceiver : uses @enduml ``` ### Entities * `pallet-liquidity-pools` is the primary communication point between Centrifuge Chain logic and the EVM through: * Calling extrinsics that trigger remote EVM contract functions (outbound) * Internal chain hooks that update the EVM state (outbound) * Processing actions from EVM domains that alter the chain (inbound) This pallet communicates with the EVM via the `pallet-liquidity-pools-gateway` by sending messages that contain information about the action. Essentially, `pallet-liquidity-pools` converts actions into messages and messages into actions. * `pallet-liquidity-pools-gateway` is the central component that links all processes together. It enqueues/dequeues messages for later processing and sends/receives these messages to/from routers that manage communication with EVM domains. The gateway is responsible for: * Batching messages * Verifying message proofs before marking a message as *valid* and then processing it * Sending proofs for a message to different routers * `pallet-liquidity-pools-gateway-queue` is responsible for storing messages to be processed later when the block has capacity, as determined by the `on_idle()` pallet method, a default Polkadot SDK pallet hook ([documentation](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.Hooks.html#method.on_idle)). Any message that needs to be dispatched or has been received is first queued. When the block has enough capacity to execute messages from the queue, they are processed one by one until the maximum block weight (configured in the runtime) is reached. Depending on the message's direction, it is either redirected to its designated EVM domain (outbound) or handled internally if Centrifuge Chain is the destination domain (inbound). * `pallet-liquidity-pools-forwarder` handles forwarded messages. It: * Checks if an incoming message from domain `B` was forwarded from source domain `A`, and if so, unwraps the `Forwarded` message to extract the original payload and the source domain `A`. * Checks if an outgoing message to destination domain `A` needs to be forwarded via intermediary domain `B`, and if so, wraps the payload into a `Forwarded` message, including source domain `Centrifuge` and the forwarding contract address on intermediary domain `B`. * `MessageSerializer` is responsible for serializing outgoing messages and deserializing incoming ones. It is a runtime type that serves as middleware between `routers` and `pallet-liquidity-pools-forwarder`, so neither needs to handle (de-)serialization. * `RouterDispatcher` is responsible for selecting the correct router based on a `router_id`. The gateway uses this entity to send messages and determine the appropriate router for a router ID. This entity is a runtime type that provides the same interface as a router for sending messages. From the gateway's perspective, it functions as a router, but in practice, it mainly forwards outbound messages. `RouterDispatcher` is used only to send messages to actual routers. In the opposite direction, when receiving messages, these routers submit messages directly to the gateway. * `routers` are pallets that can receive messages from other domains and send messages back to them. Examples of these routers include: * `pallet-axelar-router` * `pallet-snowbridge-router` _(not yet in the codebase)_ When routers receive messages from outside, they submit them to the `pallet-liquidity-pools-gateway`, identifying the message by its unique ID per pallet instance. Similarly, when the gateway sends messages to routers via the `RouterDispatcher`, routers are responsible for delivering them to the message's destination domain. #### Traits To know more about the trait methods, check: [`traits/src/liquidity_pools.rs`](https://github.com/centrifuge/centrifuge-chain/blob/main/libs/traits/src/liquidity_pools.rs) #### Basic types Types that requires spetial attention to understand Liquidity Pools * `Domain`: Represents another chain. Currently just supported our own chain and Evm chains. * `DomainAddress`: A junction of a `Domain` along with the address in that domain. This type also allow coversions. * i.e. I can create a `DomainAddress` from an ethereum address (as `H160`) and from a chain as: ```rust let address = DomainAddress::Evm(chain, eth_address) ``` later I can obtain its local representation of that account in our chain as `address.as_local()`, of type `AccountId` * `RouterId`. Represents a router, which is a *way* of sending/receiving a message to certain chain. It has the same information as a `Domain`, but also contains the information about the *medium* or the *how* the message is sent and receive to that domain. ### Actions Two main actors: * The **Centrifuge Operator** who calls extrinsics in Centrifuge Chain with the intention of perform a side effect in the EVM side. - The **Liquidity Pools Contact** on EVM, who will send messages through EVM to Centrifuge Chain to perform some action on-chain. The Queue pallet can also be seen as an "actor" that dequeing messages previously enqueued to be processed. This is only performed if the chain has enough capacity to do it after processing the block pending extrinsics (it means, if the chain is "idle"). ```plantuml @startuml skinparam sequenceArrowThickness 2 skinparam roundcorner 20 skinparam sequence { LifeLineBackgroundColor #Business } actor "Liquidity\nPools\nContact\non\nEVM" as EVM collections routers << (P, motivation) >> participant "Router \n Dispatcher" as RouterDispatcher << (T, motivation) >> participant "Message\nSerializer" as MessageSerializer << (T, motivation) >> participant Forwarder << (P,motivation) >> participant Gateway << (P,motivation) >> participant Queue << (P,motivation) >> participant "Liquidity \n Pools" as LP << (P,motivation) >> database "Centrifuge\nChain" as CentrifugeChain actor "Centrifuge\nOperator" as CentrifugeOperator == receive message == EVM -[#blue]> routers ++ : execute() routers -> MessageSerializer ++ : receive() note over MessageSerializer : deserialize MessageSerializer -> Forwarder ++ : receive() note over Forwarder : unwrap msg\nif forwarded Forwarder -> Gateway ++ : receive() Gateway -> Queue ++ : submit() note over of Queue : queue msg Gateway <-- Queue -- Forwarder <-- Gateway -- MessageSerializer <-- Forwarder -- routers <-- MessageSerializer -- deactivate == execute inbound message == ...during on_idle()... note over of Queue : unqueue msg Queue -> Gateway ++ : process() loop for any submsg in the msg Gateway -> LP ++ : handle() LP -> CentrifugeChain ++ : do_action() LP <-- CentrifugeChain -- Gateway <-- LP -- end Queue <-- Gateway -- ... ... ... == execute outbound message == CentrifugeOperator -> LP ++ : call_extrinsic() LP -> Gateway ++ : handle() alt if batching is enabled Gateway -> Queue ++ : submit() note over of Queue : queue msg Gateway <-- Queue -- else Gateway -> Gateway : add_into_batch end LP <-- Gateway -- CentrifugeOperator <-- LP -- == end batching == CentrifugeOperator -> Gateway ++ : end_batch_messages() Gateway -> Queue ++ : submit() note over of Queue : queue msg Gateway <-- Queue -- CentrifugeOperator <-- Gateway -- == send message == ...during on_idle()... note over of Queue : unqueue msg Queue -> Gateway ++ : process() Gateway -> Forwarder ++ : send() note over Forwarder : wrap msg\nif forwarding Forwarder -> MessageSerializer ++ : send() note over MessageSerializer : serialize MessageSerializer -> RouterDispatcher ++ : send() note over RouterDispatcher : choose the correct\nrouter instance RouterDispatcher -> routers ++ : send() routers -[#blue]> EVM : call() RouterDispatcher <-- routers -- MessageSerializer <-- RouterDispatcher -- Forwarder <-- MessageSerializer -- Gateway <-- Forwarder -- Queue <-- Gateway -- @enduml ``` ## Foreign Investments ```plantuml @startuml skinparam roundcorner 10 cloud "legend" { component "I'm a pallet" as a rectangle "I'm an interface" as b a -[hidden]down- b } skinparam component { BackgroundColor<<utility>> #Lavender BackgroundColor #Motivation } skinparam rectangle { BackgroundColor #Strategy } [pallet-foreign-investments] <<utility>> as fi [pallet-liquidity-pools] as lp [pallet-investments] as investments [pallet-order-book] as orders [pallet-token-mux] <<utility>> as tokenmux rectangle Investments rectangle ForeignInvestments rectangle ForeignInvestmentsHooks rectangle TokenSwaps rectangle "NotificationStatusHook\n(collected)" as collected_hook rectangle "NotificationStatusHook\n(fulfilled)" as fulfilled_hook lp .down.|> ForeignInvestmentsHooks : implements lp -down-> ForeignInvestments : uses fi .up.|> ForeignInvestments : implements fi -up-> ForeignInvestmentsHooks : uses fi .down.|> fulfilled_hook : implements fi .down.|> collected_hook : implements fi -down-> Investments : uses fi -down-> TokenSwaps : uses orders .up.|> TokenSwaps : implements orders -up-> fulfilled_hook : uses tokenmux -up-> TokenSwaps : uses investments .up.|> Investments : implements investments -up-> collected_hook : uses @enduml ``` ### Entities * The `pallet-foreign-investments` is a module without extrinsics that functions as a connector, linking investments and orders to liquidity pools through various traits: * For incoming investment deposit requests (`DepositRequest` messages), it initiates the token swap via the `TokenSwaps` trait, converting the foreign asset to the pool currency by creating or updating orders in the `pallet-orderbook`. * Upon partial fulfillment of those orders, the swapped amount is invested via `pallet-investments`. * Whenever a pending (partial) investment is processed during [pool epoch execution](https://docs.centrifuge.io/user/centrifuge-pools/epoch/), a `FulfilledDepositRequest` message is sent back to the source EVM domain to report the invested amount. * For incoming investment cancellation requests (`CancelDepositRequest` messages), it cancels any pending investment that has not been processed during the pool epoch execution and initiates a token swap from the pool currency back to the foreign asset originally used for the investment. * When the entire pending investment amount has been swapped back to the foreign asset, `pallet-foreign-investments` dispatches a `FulfilledCancelDepositRequest` message to the EVM, returning the investment. * For incoming redemption requests (`RedeemRequest` messages), it starts the redemption process by forwarding the request to `pallet-investments`. This process progresses once the corresponding pool closes and executes an epoch, resulting in the dispatch of a `FulfilledRedeemRequest`. * For incoming redemption cancellation requests (`CancelRedeemRequest` messages), it cancels any pending redemption process and immediately dispatches a `FulfilledCancelRedeemRequest`, providing information about the amount that is still redeemed. * `pallet-orderbook` is responsible for swapping two tokens, A and B. In practice, these token pairs represent foreign assets used for investments and pool currencies. It handles placing, updating, canceling, and fulfilling orders. * Whenever an order related to a foreign investment is partially fulfilled, a notification hook informs `pallet-foreign-investments` of the order details. * `pallet-token-mux` is a utility module for proxying variants of the same foreign asset (e.g., USDC) to a local asset representation. This allows pools to use a specific local asset representation as the pool currency and facilitates the deposit of foreign investments. * `pallet-investments` is responsible for managing investments in the corresponding pool's asset and handling redemptions. It creates orders for assets and allows users to collect these orders. ### Actions The following diagrams shows the sequence from the `pallet-foreign-investments` point of view and which LP messages are sent/received. #### Investments ```plantuml @startuml skinparam sequenceArrowThickness 2 skinparam roundcorner 20 skinparam sequence { LifeLineBackgroundColor #Business } actor "Liquidity Pools\nContact on EVM" as Solidity participant LiquidityPools as LP << (P, motivation) >> participant ForeignInvestments as FI << (P, motivation) >> participant Investments << (P, motivation) >> participant OrderBook << (P, motivation) >> == increase == Solidity -[#Green]> LP : DepositRequest activate LP LP -> FI ++ : increase_foreign_investment() FI -> FI : increase() activate FI #Strategy FI -> OrderBook ++ : create_or_increase_swap() FI <-- OrderBook -- deactivate FI alt "if same currencies" FI -> FI : post_increase_swap() activate FI #Strategy FI -> Investments ++ : update_investment() FI <-- Investments -- deactivate FI end LP <-- FI -- deactivate LP == cancel == Solidity -[#Green]> LP : CancelDepositRequest activate LP LP -> FI ++ : cancel_foreign_investment() FI -> FI : cancel() activate FI #Strategy FI -> Investments ++ : update_investment() FI <-- Investments -- FI -> OrderBook ++ : create_swap() FI <-- OrderBook -- deactivate FI alt "if no pool to foreign swap or if same currencies" FI -> FI : post_cancel_swap() activate FI #Strategy LP <- FI ++ #Strategy : fulfill_cancel_investment() Solidity <[#Blue]- LP : FulfilledCancelDepositRequest LP --> FI -- deactivate FI end deactivate LP LP <-- FI -- == fulfill a foreign to pool swap == hnote over OrderBook : Order partially fulfilled FI <- OrderBook ++ : fulfill() FI -> FI : post_increase_swap() activate FI #Strategy FI -> Investments ++ : update_investment() FI <-- Investments -- deactivate FI FI --> OrderBook -- == fulfill a pool to foreign swap == hnote over OrderBook : Order partially fulfilled FI <- OrderBook ++ : fulfill() FI -> FI : post_cancel_swap() activate FI #Strategy note right of LP : Called only when the\nswap is fully fulfilled LP <- FI ++ #Strategy : fulfill_cancel_investment() Solidity <[#Blue]- LP : FulfilledCancelDepositRequest LP --> FI -- deactivate FI FI --> OrderBook -- == collect == hnote over Investments : Epoch close.\nInvestment partially\ncollected FI <- Investments ++ : collect() FI -> FI : post_collect() activate FI #Strategy LP <- FI ++ #Strategy : fulfill_collect_investment() Solidity <[#Blue]- LP : FulfilledDepositRequest LP --> FI -- deactivate FI FI --> Investments -- @enduml ``` #### Redemptions ```plantuml @startuml skinparam sequenceArrowThickness 2 skinparam roundcorner 20 skinparam sequence { LifeLineBackgroundColor #Business } actor "Liquidity Pools\nContact on EVM" as Solidity participant LiquidityPools as LP << (P, motivation) >> participant ForeignInvestments as FI << (P, motivation) >> participant Investments << (P, motivation) >> participant OrderBook << (P, motivation) >> == increase == Solidity -[#Green]> LP : RedeemRequest activate LP LP -> FI ++ : increase_foreign_redemption() FI -> FI : increase() activate FI #Strategy FI -> Investments ++ : increase_redemption() Investments -> Investments : update_redemption() FI <-- Investments -- deactivate FI LP <-- FI -- deactivate LP == cancel == Solidity -[#Green]> LP : CancelRedeemRequest activate LP LP -> FI ++ : cancel_foreign_redemption() FI -> FI : cancel() activate FI #Strategy FI -> Investments ++ : cancel_redeemption() Investments -> Investments : update_redemption() FI <-- Investments -- deactivate FI LP <-- FI -- Solidity <[#Blue]- LP : FulfilledCancelRedeemRequest deactivate LP == collect == hnote over Investments : Epoch close.\nRedemption partially\ncollected FI <- Investments ++ : collect() FI -> FI : post_collect_and_swap() activate FI #Strategy FI -> OrderBook ++ : create_or_increase_swap() FI <-- OrderBook -- deactivate FI alt "if same currencies" FI -> FI : post_swap() activate FI #Strategy LP <- FI ++ #Strategy : fulfill_collect_redemption() Solidity <[#Blue]- LP : FulfilledRedeemRequest LP --> FI -- deactivate FI end FI --> Investments -- == fulfill a pool to foreign swap == hnote over OrderBook : Order partially fulfilled FI <- OrderBook ++ : fulfill() FI -> FI : post_swap() activate FI #Strategy note right of LP : Called only when the\nswap is fully fulfilled LP <- FI ++ #Strategy : fulfill_collect_redemption() Solidity <[#Blue]- LP : FulfilledRedeemRequest LP --> FI -- deactivate FI FI --> OrderBook -- @enduml ``` ## Scope | File | SLOC | |-------------------------------------------------------------|-------| | pallets/liquidity-pools-gateway-queue/src/lib.rs | 170 | | pallets/liquidity-pools-gateway/src/message.rs | 14 | | pallets/liquidity-pools-gateway/src/lib.rs | 440 | | pallets/liquidity-pools-gateway/src/message_processing.rs | 362 | | pallets/liquidity-pools/src/message.rs | 826 | | pallets/liquidity-pools/src/lib.rs | 934 | | pallets/liquidity-pools/src/hooks.rs | 96 | | pallets/liquidity-pools/src/gmpf/error.rs | 35 | | pallets/liquidity-pools/src/gmpf/ser.rs | 244 | | pallets/liquidity-pools/src/gmpf/de.rs | 193 | | pallets/liquidity-pools/src/inbound.rs | 140 | | pallets/foreign-investments/src/lib.rs | 180 | | pallets/foreign-investments/src/impls.rs | 168 | | pallets/foreign-investments/src/entities.rs | 301 | | pallets/foreign-investments/src/swaps.rs | 115 | | pallets/order-book/src/lib.rs | 591 | | pallets/token-mux/src/lib.rs | 267 | | pallets/investments/src/lib.rs | 1045 | | runtime/common/src/routing.rs | 96 | | libs/types/src/domain_address.rs | 89 | | libs/types/src/tokens.rs | 467 | | libs/types/src/investments.rs | 125 | | libs/traits/src/investments.rs | 153 | | libs/traits/src/liquidity_pools.rs | 82 | | libs/traits/src/swaps.rs | 64 | | **Total** | **7197** | ### Out of scope issues * Centrifuge Chain Runtime configuration of pallets * However, findings on possible misconfiguration of pallets in scope will be considered * Any issue that requires governance or admin actions * Adding assets is controlled by CFG governance, and governance will only add standard tokens (limited to reasonable decimals i.e. <= 18), no rebasing tokens, fee-on-transfer tokens, tokens with callbacks, etc. * Any issues from Solidity LP audits that also apply to logic of Rust code * Any sections of the files in scope under `#[cfg(test)]` or feature-gated by `runtime-benchmark` * Overestimated weights * Forwarding not implemented on Solidity side * Tranche tokens can be stuck if a cross-chain transfer is performed to a destination that is not a member, or an invalid domain, or an invalid address * Gas for routing is not paid automatically * Batches can be submitted that are overweight on the destination domain * Liquidity can be locked if a pool admin disallows all assets * Investments are only fulfilled if the `collect_investments/redemptions_for` method is triggered by a user or bot on Centrifuge Chain ## How to build The Liquidity Pools protocol involves interaction between Solidity contracts on the EVM side and the Centrifuge Chain. Setting up both environments locally is beyond the scope of this audit. However, if you are interested in experimenting with the setup, we recommend reviewing [our LP integration tests](https://github.com/centrifuge/centrifuge-chain/blob/main/runtime/integration-tests/src/cases/lp/investments.rs#L412), which cover the complete LP setup, including the EVM contracts. For isolated unit tests, you can take a look at the `tests.rs` files of the pallets in scope. However, note that tests and mocks are not in scope. ### Using Chopsticks If you still wish to run the Centrifuge Chain locally, we suggest using [Chopsticks](https://github.com/AcalaNetwork/chopsticks?tab=readme-ov-file#quick-start) for that purpose. We have recently updated our Centrifuge config. Unfortunately, this has not been released (current version [v0.13.3](https://github.com/AcalaNetwork/chopsticks/releases)) such that you need to download that config locally from the Chopsticks repository [here](https://github.com/AcalaNetwork/chopsticks/blob/master/configs/centrifuge.yml). ```bash npx @acala-network/chopsticks@latest -c configs/centrifuge.yml ``` The config funds the well-known `Alice` key with 1M CFG. ```bash Secret Key URI `//Alice` is account: Network ID: substrate Secret seed: 0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a Public key (hex): 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d Account ID: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d Public key (SS58): 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY SS58 Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY ``` ### How to run integration tests :::info :bulb: LP integration tests require Foundry ([official documentation](https://book.getfoundry.sh/getting-started/installation#installation)) to be installed on your system because otherwise the LP contracts from the git submodule `liquidity-pools` cannot be built leading to the error message from the `LP_SOL_SOURCES` constant: > Build script failed to populate environment variable LP_SOL_SOURCES pointing to missing solidity source files in the 'target/*/build/integration-tests*/out' directory required for EVM integration tests.\n\nPlease check if you have pulled the 'liquidity-pools' submodule via `git pull --recurse-submodules` and if you have installed the forge cli, e.g. check `forge -V`."); Moreover, you need to initialize the submodule via ``` git submodule update --init --recursive git pull --recurse-submodules ``` ::: Please note that we do not recommend running all integration tests since that will take a while but rather focus on a single one to save time: ```bash # Run all LP integration tests cargo test -p runtime-integration-tests cases::lp:: # Run an isolated LP integration tests cargo test -p runtime-integration-tests [<test-name>] ``` ### How to run unit tests ```bash # Run all pallet tests cargo test -p <pallet-name> # Run an isolated UT cargo test -p <pallet-name> [<test-name>] # Run all unit tests cargo test --workspace --release --features try-runtime --exclude runtime-integration-tests ```

Import from clipboard

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lost their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.


Upgrade

All
  • All
  • Team
No template.

Create a template


Upgrade

Delete template

Do you really want to delete this template?

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Tutorials

Book Mode Tutorial

Slide Example

YAML Metadata

Resources

Releases

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions

Versions and GitHub Sync

Sign in to link this note to GitHub Learn more
This note is not linked with GitHub Learn more
 
Add badge Pull Push GitHub Link Settings
Upgrade now

Version named by    

More Less
  • Edit
  • Delete

Note content is identical to the latest version.
Compare with
    Choose a version
    No search result
    Version not found

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub

      Please sign in to GitHub and install the HackMD app on your GitHub repo. Learn more

       Sign in to GitHub

      HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Available push count

      Upgrade

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Upgrade

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully