This repo contains several substrate pallet implementation for Sygma protocol.
Sygma is a multi-purpose interoperability layer that supports developers in building cross-chain dApps.
- Build locally
$ make build
- Build docker image
$ docker build -t sygma-substrate-pallet .
- Run unit tests
$ make test
- Run local testnet with Sygma protocol integrated
$ make start-dev
- Run docker container as local testnet
$ docker run -p 9944:9944 -it sygma-substrate-pallet --dev --rpc-external
Explore testnet at 127.0.0.1:9944
In the substrate pallet, there are few concepts that are significantly important yet confusing, thus, in this section, we are going to make some clarification and explanation.
MultiLocation
is a substrate type. It is introduced by XCM, and it is used to identify any single entity location that exists within the world of Polkadot consensus.
MultiLocation
always expresses a relative location to the current location. Practically, MultiLocations
are used to identify places to send XCM messages.
In Sygma pallets, it is used to identify the destination when depositing, the deposit extrinsic signature is shown below:
pub fn deposit(origin: OriginFor<T>, asset: MultiAsset, dest: MultiLocation) -> DispatchResult
dest: MultiLocation
here is able to include any customized data in any desired layer. The logic to extract the detail is depending on the trait ExtractDestinationData
implementation
ExtractDestinationData
trait currently has only one method extract_dest
, and the current implementation takes a MultiLocation
and extracts both recipient address as Vec<u8>
and dest domainID as DomainID
pub trait ExtractDestinationData {
fn extract_dest(dest: &MultiLocation) -> Option<(Vec<u8>, DomainID)>;
}
As a developer who needs to construct dest: MultiLocation
and then call deposit
, you need to know how this MultiLocation is structured, for example:
(parents: 0, interior: X2(GeneralKey("ethereum recipient"), GeneralIndex(destDomainID)))
MultiAsset
is also a substrate type. Asset can be divided into different types from different point of view, such as fungible and non-fungible assets,
native asset and foreign asset ,etc. MultiAsset
is the concept to handler multiple assets in the Polkadot world.
In sygma pallets, MultiAsset
is used to identify the asset no matter its location and fungibility. Below is the definition of MultiAsset
:
pub struct MultiAsset {
pub id: AssetId,
pub fun: Fungibility,
}
the AssetID
and Fungibility
type are defined as:
pub enum AssetId {
Concrete(MultiLocation),
Abstract(Vec<u8>),
}
pub enum Fungibility {
Fungible(#[codec(compact)] u128),
NonFungible(AssetInstance),
}
the assetID is a MultiLocation
and fungibility contains u128
which is the asset amount if it's a fungible asset.
As a developer who needs to construct asset: MultiAsset
and then call deposit
, you need to know how this MultiAsset is structured, for example, the fungible testing asset USDC can be constructed like:
(
Concrete(
MultiLocation::new(1, X3(Parachain(2004), GeneralKey("sygma"), GeneralKey("usdc")))
),
Fungible(amount)
)
In sygma pallets, multiple destination domain is supported in one single pallet instance. There are DestDomain management extrinsics to register/unregister domainID with its corresponding ChainID. This information is stored in the chain storage:
pub type DestChainIds<T: Config> = StorageMap<_, Twox64Concat, DomainID, ChainID>;
ChainID is not explicitly used in the pallet logic, but they are registered with DomainID. By querying the getter method of dest_chain_ids
, it would be easy to find out which domainID is binding with which chainID.
ResourceID the identifier of the asset in sygma system. To link it with XCM asset, there is ResourcePairs
defined in the runtime which is the mapping between AssetId
and ResourceID
.
type ResourcePairs: Get<Vec<(AssetId, ResourceId)>>;
As mentioned in the MultiAsset
section, the AssetId
contains the asset's MultiLocation, so that one asset with its MultiLocation
is able to link with ResouceID
,
AssetID of u32: When creating an asset in substrate, a u32
number has to be assigned as the assetID
and this number will be displayed in the assets
page on Polkadot JS App. This assetID
is used when transferring this asset between different accounts within the parachain. It is normally used
in assets
pallet.
AssetID of xcm: This is the xcm type of AssetID. It is the first parameter of MultiAsset
. In Sygma pallets, it has an alise as XcmAssetId
. XcmAssetId
is the asset identifier within the entire Polkdaot world,
because under the hood, it is a MultiLocation
(see definition above).
It is also important to understand the relationship between an asset
, the u32 assetID
, the XcmAssetID
, the MultiLocation
of the asset, the basic fee associated with the asset,
and how the asset is binding with a ResourceID
:
In Sygma pallets, u32
AssetID is binding with Asset MultiLocation by the implementation of SimpleForeignAssetConverter
struct, it implements the xcm-exectuor::Convert
trait. Since XcmAssetID
contains the Asset MultiLocation, it can now associate with basic fee together with the fee amount(u128
).
Similarly, Asset MultiLocation can also map with ResouceID
in the ResourcePairs: Vec<(XcmAssetId, ResourceId)>
.
In summary, MultiLocation
of an Asset
is the key in this relationship.
Please note, AssetID
does not necessarily have to be u32
, it could be defined as u8
or u128
, etc. as well. In Sygma pallet, we defined it as u32
for the demo purpose because
sygma pallets use pallet-assets
to manage foreign assets in the demo runtime. Other substrate chain developers could config their own implementation, for example, some parachains use orml
token,
which will have its own concept.
When sending and receiving over the network, substrate uses an encoding and decoding program called SCALE codec. The SCALE codec is not self-describing. It assumes the decoding context has all type knowledge about the encoded data. In general, each data type has its own rule when encoding by SCALE, so when decoding, they will follow their own rule based on its data type.
It is not recommended to do the manual decoding; however, it is important to understand the underline mechanism.
The substrate reference table for this encoding/decoding
rules can be found here.
There are other language lib that has implemented SCALE codec can be used when interacting with substrate node which can also be found in the link above.