dApp Integration
9.1 How Delivery Works
When a relay miner wins an auction for a cross-chain message, it calls receiveEntangleMessage() on the destination dApp contract — not the Entangle contract. The dApp owns this function and implements its own application logic inside it.
Relay miner
→ dApp.receiveEntangleMessage(payload, seq_no, src_chain, src_addr, sig_bundle)
Inside dApp.receiveEntangleMessage():
1. Check dedup: require(!executed[seq_no], 'ALREADY_EXECUTED')
2. Build hash: msg_hash = hash(src_chain, dst_chain, seq_no, src_addr, dst_addr, payload)
3. Validate: require(entangle.verifyMessage(msg_hash, sig_bundle))
4. Mark done: executed[seq_no] = true
5. App logic: _handleMessage(payload) // your custom logic here
9.2 EVM Integration (Solidity)
Step 1: Import the Entangle interface
// SPDX-License-Identifier: MIT
interface IEntangle {
function verifyMessage(bytes32 msgHash, bytes calldata sigBundle)
external view returns (bool);
}
Step 2: Implement your dApp contract
contract MyDApp {
IEntangle public immutable entangle;
mapping(uint64 => bool) public executed;
constructor(address _entangle) {
entangle = IEntangle(_entangle);
}
// Source: send a message to another chain
function sendCrossChainMessage(
string calldata dst_chain,
address dst_addr,
bytes calldata payload
) external payable {
uint256 fee = IEntangle(address(entangle)).getTotalFee(dst_chain);
require(msg.value >= fee, "INSUFFICIENT_FEE");
IEntangle(address(entangle)).sendMessage{value: fee}(
dst_chain, dst_addr, payload
);
}
// Destination: receive a message from another chain
function receiveEntangleMessage(
bytes calldata payload,
uint64 seq_no,
string calldata src_chain,
bytes calldata src_addr,
bytes calldata sig_bundle
) external {
require(!executed[seq_no], 'ALREADY_EXECUTED');
bytes32 msg_hash = keccak256(abi.encode(
src_chain, block.chainid, seq_no, src_addr,
address(this), payload
));
require(entangle.verifyMessage(msg_hash, sig_bundle), 'BAD_SIGS');
executed[seq_no] = true;
_handleMessage(payload); // implement your logic here
}
function _handleMessage(bytes calldata payload) internal {
// your application logic
}
}
Step 3: Query the current fee before sending
Always call getTotalFee(dst_chain) before presenting a send UI — fees change with the gas oracle:
const fee = await entangle.getTotalFee("arbitrum");
await myDApp.sendCrossChainMessage("arbitrum", recipientAddr, payload, { value: fee });
9.3 Integration Summary by Ecosystem
| Ecosystem | Relay miner calls | dApp calls for validation | Sig scheme |
|---|---|---|---|
| EVM (Ethereum, Arbitrum…) | dApp.receiveEntangleMessage() |
entangle.verifyMessage() |
secp256k1 ECDSA |
| Solana | receiveEntangleMessage instruction |
Entangle program verify_message CPI |
ed25519 |
| SUI | dApp Move function receiveEntangleMessage |
Entangle Move verify_message call |
ed25519 |
| Cosmos / IBC | dApp CosmWasm receiveEntangleMessage |
Entangle CosmWasm verify_message |
secp256k1 ECDSA |
| Stellar | dApp contract receiveEntangleMessage |
Entangle contract verify_message |
ed25519 |
9.4 Adding New Chains
Same-ecosystem chain (e.g., a new EVM L2): Deploy the Entangle contract and add to the Chain Configuration Registry. No code changes required.
New ecosystem chain: Requires a new ChainAdapter implementation, an SDK PR, and a governance vote to add it to the chain registry.