Relay Validators

WIP

The relay validators are comparatively thin. Each validator is comprised of

  1. a set of accounts one per account standard,

  2. a set of private keys for signing transactions in the bridged blockchains,

  3. logic (implemented as "helpers") is required for

    1. listening and

    2. relaying events from the source to the target blockchain.

Validators' code distribution

For security & reliability purposes part of the validators' functionality resides in the bridged blockchains inside the bridge pallet for XP.network and inside the smart contracts for the other ledgers.

Blockchain connection settings:

Connection parameter

Value

Transaction Port

3001

XP.network testnet

https://explorer.xp.network

Elrond Smart Contract

erd1qqqqqqqqqqqqqpgqfy5zmm64avweyq3rcw65xczwkwedfz5zs3ysmja8la

HECO SC

0x8b9c95147C185A9d0940DC26a6EA774eE05D8853

BSC SC

0x5b5C8b16937F60D02aFaC76bf6614c33911636FC

Ropsten SC

0xf6fceC833bFb9bd26a898143A6b41799F5Abfe0f

Signature

class ElrondHelper implements
        ChainListener<
                // Listens to the events:
                TransferEvent         | 
                TransferUniqueEvent   | 
                UnfreezeEvent         | 
                UnfreezeUniqueEvent, 
                TransactionHash>,
        ChainEmitter<
                // Emits events:
                string, 
                void, 
                TransferEvent         | 
                TransferUniqueEvent   | 
                UnfreezeEvent         | 
                UnfreezeUniqueEvent>,
        ChainIdentifier
{ ... }

Functionality

Verifies the following transactions:

  1. eGLD transfer to a foreign blockchain

  2. ESDT NFT transfer to a foreign chain

  3. minting wrapped semi-fungible tokens to an account on Elrond e.g.: (XPNET-<hex>-<nonce>)

  4. minting wrapped NFT to an account in Elrond

Signature

class PolkadotPalletHelper
    implements
        ChainEmitter<
            // Emits the events:
            EventRecord,
            void,
            TransferEvent         | 
            TransferUniqueEvent   | 
            UnfreezeEvent         | 
            UnfreezeUniqueEvent
        >,
        ChainListener<
            // Listens to the events:
            TransferEvent         | 
            TransferUniqueEvent   | 
            UnfreezeEvent         | 
            UnfreezeUniqueEvent, 
            Hash>,
        ChainIdentifier
{

Functionality

Verifies the following transactions:

  1. XPNET transfer to a foreign blockchain

  2. NFT transfer to a foreign chain

  3. minting wrapped fungible tokens to an account on XP.network

  4. minting wrapped NFT to an account on XP.network

3. Web3Helper

Signature

class Web3Helper
implements ChainEmitter<
                // Emits events
                SolEvent, 
                void, 
                TransferEvent | 
                UnfreezeEvent
                >, 
           ChainListener<
                // Listens to events
                TransferEvent | 
                UnfreezeEvent, 
                string
                >, ChainIdentifier 
{ ... }

Functionality

Verifies the following transactions:

  1. Native Tokens transfer to a foreign blockchain

  2. NFT transfer to a foreign chain

  3. minting wrapped fungible tokens to an account on a web3 compatible chain

  4. minting wrapped NFT to an account on a web3 compatible chain

4. Validator Events

Custom types and dependency injection interfaces used in the module:

// ===================== Interfaces: ==========================

// Ensures an entity has a 'chain_nonce' data field
interface MultiChainEvent {readonly chain_nonce: number;}

// Ensures an entity has a 'chainNonce' data field
interface ChainIdentifier  {readonly chainNonce:  number;}

// Ensures an entity has a 'toString()' method
interface IntoString {toString(): string;}

// A bridged blockchain that emits events
interface ChainEmitter<
    EmissionEvent,     // Raw event
    Iter,              // Iterator over the raw event
    SupportedEvents    // [[TransferEvent]], [[UnfreezeEvent]], [[ScCallEvent]]
    > {
    // Event iterator over raw events
    eventIter(cb: (event: EmissionEvent) => Promise<void>): Promise<Iter>;
    // Convert a raw event to a known event
    eventHandler(event: EmissionEvent): Promise<SupportedEvents | undefined>;
}

// Ensures an entity has an 'emittedEventHandler' method
interface ChainListener<
    SupportedEvents,             // [[TransferEvent]], [[UnfreezeEvent]], [[ScCallEvent]]
    TxnHash                      // a TX hash
    > {
    emittedEventHandler(
        event: SupportedEvents,  // [[TransferEvent]], [[UnfreezeEvent]], [[ScCallEvent]]
        origin_nonce: number     // the original blockchain ID
        ): Promise<TxnHash>;     // returns a TX hash in a Promise
}


// =========================== Types: ==========================

// Union type comprising all the events & event handlers for a chain
declare type FullChain<Event, Iter, Handlers, Tx extends IntoString> = 
    ChainEmitter<Event, Iter, Handlers> &
    ChainListener<Handlers, Tx> &
    ChainIdentifier;

// A chain mapper type
type ChainMap<Event, Iter, Handlers, Tx extends IntoString> = {
    [index: number]: FullChain<Event, Iter, Handlers, Tx>;
}

4.1. TransferEvent

An event of fungible liquidity transfer. It indicates that the tokens were locked in the source chain and are ready to be minted as wrapped tokens in the target blockchain and released to a designated account.

Signature:

class TransferEvent implements MultiChainEvent { ... }

Inner fields:

readonly     action_id:     BigNumber;    // an action ID
readonly     chain_nonce:   number;       // a unique target chain ID
readonly     to:            string;       // target account
readonly     value:         BigNumber;    // amount of transmitted tokens

4.2 TransferUniqueEvent

An event for non-fungible tokens moving from the original to a foreign blockchain. It indicates that an NFT was locked in its original chain and is ready to be minted as wrapped NFTs and released to a target account on a target blockchain.

Signature:

class TransferUniqueEvent implements MultiChainEvent { ... }

Inner fields:

readonly     action_id:     BigNumber;    // an action ID
readonly     chain_nonce:   number;       // a unique target chain ID
readonly     to:            string;       // target account
readonly     id:            Uint8Array;   // NFT identifier (see below)
// For XP.network - it is a hash
// For Elrond it is an ID + nonce
// For web3 compatible chains it is:
//     1. for ERC-721  tokens: contract address
//     2. for ERC-1155 tokens: contract address + ID

4.3. UnfreezeEvent

An event of fungible liquidity transfer. It indicates that the tokens were burned in a foreign chain and are ready to be unlocked as native tokens in the original blockchain and released to a designated account.

Signature:

class UnfreezeEvent implements MultiChainEvent { ... }

Inner fields:

readonly     action_id:     BigNumber;    // an action ID
readonly     chain_nonce:   number;       // a unique target chain ID
readonly     to:            string;       // target account
readonly     value:         BigNumber;    // amount of transmitted tokens

4.4 UnfreezeUniqueEvent

An event for non-fungible tokens returning to the original blockchain. It indicates that an NFT was burned in a foreign chain and is ready to be unlocked as native NFTs and released to a target account on the original blockchain.

Signature:

class UnfreezeUniqueEvent implements MultiChainEvent { ... }

Inner fields:

readonly     action_id:     BigNumber;    // an action ID
readonly     chain_nonce:   number;       // a unique target chain ID
readonly     to:            string;       // target account
readonly     id:            Uint8Array;   // NFT identifier (see below)
// For XP.network - it is a hash
// For Elrond it is an ID + nonce
// For web3 compatible chains it is:
//     1. for ERC-721  tokens: contract address
//     2. for ERC-1155 tokens: contract address + ID

4.5 Event Emission

The function call signature:

export async function emitEvents<Handlers extends MultiChainEvent>(
    // The socket: Server<ServerEvents>
    io: TxnSocketServe, 
    // Bridged blockchain array
    chains: Array<FullChain<any, any, Handlers, IntoString>>
    // 
): Promise<void>

Internal fields

// Internal chain mapper
const map: ChainMap<any, any, Handlers, IntoString> = {};

Internal subfunctions

// Event handler function
const handleEvent = async (
    // Chain listener handlers
    listener:      ChainListener<Handlers, IntoString> & ChainIdentifier, 
    // The handled event
    event:         Handlers,
    // The ID of the original blockchain
    origin_nonce:  number
    
    ) => {
    
        const tx = await listener.emittedEventHandler(event, origin_nonce);
        
        if (event instanceof TransferUniqueEvent) {
        
            io.emit(
                "transfer_nft_event", 
                listener.chainNonce, 
                event.action_id.toString(), 
                tx.toString());
                
        } else if (event instanceof UnfreezeUniqueEvent) {
        
            io.emit(
                "unfreeze_nft_event", 
                listener.chainNonce, 
                event.id.toString(), 
                tx.toString());
        }
    }

Listening to the events of the chains in a loop

for (const chain of chains) {
        if (map[chain.chainNonce] !== undefined) {
            throw Error("Duplicate chain nonce!")
        }
        map[chain.chainNonce] = chain;
        listenEvents(chain);
}

Last updated

Was this helpful?