Symbol Middleware
Idea inspiration
When I was conducting XYM payment tours in Japan. I noticed many restaurants accepting XYM as payment, Additionally, there are a few wallet applications widely used by community members.
As XYM payments grow in popularity, bad actors may attempt scams in the future. I wondering if a middleware service could check the payment address before transferring XYM.
What is Symbol Middleware?
The concept of Symbol middleware is to act as a checkpoint that allows for specific validation rules. It can validate data, such as addresses or mosaics(tokens).
Symbol middleware design
The idea is to store the Merkle leaf and Merkle root on the chain, by using the symbol account metadata.
- Valid data (leaf) can be uploaded to NFTDrive or IPFS storage
- NFTDrive / IPFS URL pointer and Merkle root hash can be stored in Account Metadata
How it works?
In this article, I'll use restaurant payment addresses as an example for middleware services. The following steps outline the process:
1. Create a Merkle root hash
import { MerkleTree } from 'merkletreejs';
import keccak256 from 'keccak256';
// Example addresses (should be uploaded to NFTDrive/IPFS for real use)
const addresses = [
'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', // BTC Address
'0xe688b84b23f322a994A53dbF8E15FA82CDB71127', // ETH address
'TCMSZGQAEGBZ6AIIY5ZTACYOK2Q5F4QLMJR6KYQ', // Symbol Testnet Address
'NASYMBOLLK6FSL7GSEMQEAWN7VW55ZSZU25TBOA', // Symbol Mainnet Address
]
// Create leaves and Merkle tree
const leaves = addresses.map((address) => keccak256(address));
const merkleTree = new MerkleTree(leaves, keccak256, { sortPairs: true });
// Generate Merkle root
const rootHash = merkleTree.getRoot().toString('hex');
console.log('Merkle Root Hash:', rootHash);
2. Store the Merkle root hash in metadata
import { PrivateKey, utils } from 'symbol-sdk';
import { SymbolFacade, NetworkTimestamp, metadataGenerateKey } from 'symbol-sdk/symbol';
// Setup
const facade = new SymbolFacade('testnet');
const account = facade.createAccount(new PrivateKey('Your_private_key'));
// Create metadata value
const merkleTreeHash = 'root_hash_generate_by_merkle_tree';
const metadataValue = utils.hexToUint8(merkleTreeHash);
// Prepare transaction
const metadata = facade.transactionFactory.createEmbedded({
type: 'account_metadata_transaction_v1',
signerPublicKey: account.publicKey,
targetAddress: account.address,
scopedMetadataKey: metadataGenerateKey('address'), // Generate scopedMetadataKey
valueSizeDelta: metadataValue.length,
value: metadataValue,
})
const transaction = facade.transactionFactory.create({
type: 'aggregate_complete_transaction_v2',
signerPublicKey: account.publicKey,
deadline: facade.now().addHours(2).timestamp,
transactionsHash: SymbolFacade.hashEmbeddedTransactions([metadata]),
transactions: [metadata],
fee: BigInt(500000)
})
// Sign and Announce Transaction
const signature = account.signTransaction(transaction);
const jsonPayload = facade.transactionFactory.static.attachSignature(transaction, signature);
// Announce to network
(async () => {
await fetch('https://201-sai-dual.symboltest.net:3001/transactions', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: jsonPayload,
}).then(res => res.json());
})()
3. Retrieve Metadata for Verification
# scopedMetadataKey (address) = 9A01022431B77F83
# targetAddress (use for middleware service) = TCZTBI7HCCFPREBWJPZIPJA3FVKC2S2NSSPJ6YQ
curl --location 'https://201-sai-dual.symboltest.net:3001/metadata?scopedMetadataKey=9A01022431B77F83&targetAddress=TCZTBI7HCCFPREBWJPZIPJA3FVKC2S2NSSPJ6YQ'
Extract the value field containing the Merkle root hash:
value: 998C6A47BB830C2767E297D4FDA25BD757EEA3F47DCCE782C11664C0A1217983
4. Verify Address
import { MerkleTree } from 'merkletreejs';
import keccak256 from 'keccak256';
const addresses = [
'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
'0xe688b84b23f322a994A53dbF8E15FA82CDB71127',
'TCMSZGQAEGBZ6AIIY5ZTACYOK2Q5F4QLMJR6KYQ',
'NASYMBOLLK6FSL7GSEMQEAWN7VW55ZSZU25TBOA',
]
const leaves = addresses.map((address) => keccak256(address));
const merkleTree = new MerkleTree(leaves, keccak256, { sortPairs: true });
const addressToVerify = addresses[0];
const leaf = keccak256(addressToVerify);
const proof = merkleTree.getProof(leaf);
const isValid = merkleTree.verify(proof, leaf, 'your_merkle_tree_root_from_metadata');
console.log("Is Valid:", isValid);
Conclusion
I believe middleware service will improve the security for users, and it can be used for any wallet. If we find the bad actor address and update the middleware, it will prevent users lost funds.
This is just a conceptual idea—your feedback is welcome!