メインコンテンツまでスキップ

06 · 環境設定と受入検証

公開環境へ接続するためのネットワークパラメータ、デモ資産、照会例、受入チェックリストを提供します。

6.1 環境情報

項目テスト環境メインネットデモ環境
用途開発、結合テスト、受入BOT Mainnet 機能検証
ネットワークBohr TestnetBOT Mainnet
状態利用可能デモ Suite デプロイ済み
Chain ID968677
RPChttps://rpc.bohr.lifehttps://rpc.botchain.ai
Explorerhttps://scan.bohr.lifehttps://scan.botchain.ai
ネイティブ gas 資産Test BOTBOT

Bohr Testnet と BOT Mainnet のコントラクトアドレス、Token 残高、ID、Claim は共有されません。

6.2 推奨設定構造

環境ごとに独立した設定ファイルまたは環境変数を使用します。

type RwaEnvironment = {
chainId: number;
rpcUrl: string;
explorerUrl: string;
contracts: {
token: string;
identityRegistry: string;
onchainIdGateway?: string;
idFactory?: string;
claimIssuer?: string;
};
};

テスト環境:

const rwaTestnet = {
chainId: 968,
rpcUrl: 'https://rpc.bohr.life',
explorerUrl: 'https://scan.bohr.life',
contracts: {
token: '0x922835859623d6F3b99a2742D585E093bBA0a740',
identityRegistry: '0x17e33C394Ab783D6786011d01681D8Ad540C5F53',
onchainIdGateway: '0x3B0FE2CD501534cbcbD1F97ec823Da535fc0224f',
idFactory: '0xb4E03AB303f4c42dbced87b65e1d9f5788EAb369',
claimIssuer: '0x67EC753196D6Cd1B65a29b6ff9c1bE41E554a5a7',
},
};

BOT Mainnet 機能デモ:

const rwaMainnet = {
chainId: 677,
rpcUrl: 'https://rpc.botchain.ai',
explorerUrl: 'https://scan.botchain.ai',
contracts: {
token: '0x9115A020821fDFdf265Eea8788a500d17557c370',
identityRegistry: '0x7752dd107CDec2dF5feD585BCC70457c5312021a',
onchainIdGateway: '0xa6B4E53128915E0f980DD455De9F70B6Dc61E1ee',
idFactory: '0x124ED389600870D1a5f86f657896Add3E623ca9e',
claimIssuer: '0x3950eB42733b3eF6744E8788C741788CCF6c84b3',
},
};

メインネット設定が不足または検証失敗した場合、テストネットアドレスへ自動 fallback してはいけません。メインネット JSON を参照してください。

6.3 公開デモ資産

項目
名称Project Real World Asset
シンボルPRWA
Decimals6
適格性 TopicKYC_APPROVED
Topic 値7151541257903274562366177585585886223835338030660388602590463654390840817671
Claim scheme1
許可国コード156, 702

6.3.1 Bohr Testnet アドレス

コントラクトアドレス
Token0x922835859623d6F3b99a2742D585E093bBA0a740
IdentityRegistry0x17e33C394Ab783D6786011d01681D8Ad540C5F53
IdentityRegistryStorage0xB834C4FEEADC24A9c5B873e8887CD9b54C40E98D
ClaimTopicsRegistry0xbf8adD07A8722d06c218A3A7DD4Bb44d3a90948A
TrustedIssuersRegistry0x94ffe710F64AB382274A53C8b10003a3287E621D
ModularCompliance0x1E2e2a848b611C121C86F77C703B8DD32DCdE37A
Token ONCHAINID0x551676B9061b5324676BBbB5587433454EE54521
共有コントラクトアドレス
ONCHAINID Gateway0x3B0FE2CD501534cbcbD1F97ec823Da535fc0224f
IdFactory0xb4E03AB303f4c42dbced87b65e1d9f5788EAb369
ClaimIssuer0x67EC753196D6Cd1B65a29b6ff9c1bE41E554a5a7
TREXGateway0x82e1Fa8022BA1dcFC05061E0A1CE828C189108A9
Country Allowlist0x532bFc29B0D9a281084203303a8E687259D57149

6.3.2 BOT Mainnet デモアドレス

コントラクトアドレス
Token0x9115A020821fDFdf265Eea8788a500d17557c370
IdentityRegistry0x7752dd107CDec2dF5feD585BCC70457c5312021a
IdentityRegistryStorage0xD54fD1381c970F19366cb7c50072295045ff5516
ClaimTopicsRegistry0x645E33c8c67D3Ea478F87b70d76711E110953FB9
TrustedIssuersRegistry0x4d96f4Bb96795a3EFeb3Cea43459E2ee4C5ad439
ModularCompliance0xB8A4b2aFd8318e61cDbaEC07559B49bE6bBE20D5
Token ONCHAINID0x3677B0F87892B10F1341DB3348dFEcb9CE039146
共有プラットフォームアドレス
ONCHAINID Gateway0xa6B4E53128915E0f980DD455De9F70B6Dc61E1ee
IdFactory0x124ED389600870D1a5f86f657896Add3E623ca9e
ClaimIssuer0x3950eB42733b3eF6744E8788C741788CCF6c84b3
TREX ImplementationAuthority0x3E19c7f1e6E53125fF205591fc516e25541d21ff
ONCHAINID ImplementationAuthority0x992CCAC1A33EFdb58b9cfD49CF510d5db15Fc249
IAFactory0xf3BdD013eb6c49C23f5b8E70bE5F6C8c98c76028
TREXFactory0xf622AbFDf470912CCb86A54d52D2430951a3e956
TREXGateway0x3c209A13fc9E91B060C7672874895E117a1A3Ab5
Country Allowlist0xA41A626bcBF57B3695539f6516E3D482dCFAEDEC

BOT Mainnet デモ Suite の Country Allowlist モジュールは本番監査を完了していません。技術機能検証専用であり、実資産を直接載せてはいけません。

Bohr Testnet JSON · BOT Mainnet JSON

6.4 公開照会ウォレット

サンプルウォレットTestnet ONCHAINID適格性
Alice0xb450970Eece71044acF7cB7f53Ee51e3BC2EA9220xfAB41B1DfD098F439D75cEA0cb48f70B46052544156true
Bob0xfc996C5378fe0cd03Fd4296075abc07B16b2B12F0xd3b816e7494e918297E3553d65488a7E0682C6A3702true
Cathy0x2b981b025397AE1AFca6f8DC7A2f5C7A868dEe780xb7522A6649a20A0820E292D8D0f35aB792812011156true

秘密鍵は提供されません。write 操作には自分のウォレットを使用してください。

サンプルウォレットBOT Mainnet ONCHAINIDisVerified
Alice0xb450970Eece71044acF7cB7f53Ee51e3BC2EA9220x95d0ae16d03A26BA6a0dA844938C5c50e16e407C156true
Bob0xfc996C5378fe0cd03Fd4296075abc07B16b2B12F0x3F5059a5eadc24c27809f481e97Bef4834DB4272702true
Cathy0x2b981b025397AE1AFca6f8DC7A2f5C7A868dEe780x29E0cb659BEd6baF5a6Dc19C50cB41dbBdd41194156true

negative test には自分が管理する未登録ウォレットを使用します。期待値は contains == falseisVerified == false です。ゼロアドレスを実業務ウォレットに使用しないでください。

6.5 第 1 段階:ネットワークとコントラクトを検証

例は ethers.js v5 を使用します。v6 では JsonRpcProviderformatUnitsparseUnits の v6 import を使用してください。

const { ethers } = require('ethers');
const provider = new ethers.providers.JsonRpcProvider('https://rpc.bohr.life');
const network = await provider.getNetwork();
if (network.chainId !== 968) {
throw new Error(`Wrong network: expected 968, got ${network.chainId}`);
}
const TOKEN = '0x922835859623d6F3b99a2742D585E093bBA0a740';
const IR = '0x17e33C394Ab783D6786011d01681D8Ad540C5F53';
if (await provider.getCode(TOKEN) === '0x') {
throw new Error('Token contract not found on current network');
}

write 操作の前に毎回 Chain ID を検証します。

6.6 第 2 段階:資産情報を読み取る

const token = new ethers.Contract(TOKEN, [
'function name() view returns (string)',
'function symbol() view returns (string)',
'function decimals() view returns (uint8)',
'function totalSupply() view returns (uint256)',
'function identityRegistry() view returns (address)',
'function compliance() view returns (address)',
'function paused() view returns (bool)',
], provider);

const [name, symbol, decimals, supply, registry, compliance, paused] =
await Promise.all([
token.name(), token.symbol(), token.decimals(), token.totalSupply(),
token.identityRegistry(), token.compliance(), token.paused(),
]);
console.log({
name, symbol, decimals,
totalSupply: ethers.utils.formatUnits(supply, decimals),
registry, compliance, paused,
});

名称・シンボルが公開情報と一致し、decimals を読み取れ、identityRegistry()compliance() がアドレス表に一致することを確認します。

6.7 第 3 段階:残高と適格性を照会

const wallet = '0xb450970Eece71044acF7cB7f53Ee51e3BC2EA922';
const ir = new ethers.Contract(IR, [
'function contains(address) view returns (bool)',
'function isVerified(address) view returns (bool)',
'function identity(address) view returns (address)',
'function investorCountry(address) view returns (uint16)',
], provider);
const [rawBalance, contains, verified, identity, country] =
await Promise.all([
token.balanceOf(wallet), ir.contains(wallet), ir.isVerified(wallet),
ir.identity(wallet), ir.investorCountry(wallet),
]);
console.log({
balance: ethers.utils.formatUnits(rawBalance, decimals),
contains, verified, identity, country,
});

公開テストウォレットでは containsverifiedtrue で、ID と国コードが表に一致する必要があります。

6.8 第 4 段階:イベントを照会

const latest = await provider.getBlockNumber();
const logs = await token.queryFilter(
token.filters.Transfer(), Math.max(0, latest - 5000), latest
);
for (const log of logs) {
console.log({
txHash: log.transactionHash,
blockNumber: log.blockNumber,
from: log.args.from,
to: log.args.to,
value: ethers.utils.formatUnits(log.args.value, decimals),
});
}

長時間稼働するインデクサーは範囲を分割し、checkpoint から再開して過大なリクエストを避けます。

6.9 write 操作の結合テスト

操作署名者前提
ONCHAINID 作成投資家テストウォレットtest gas
addClaimONCHAINID 管理ウォレットtest Claim 署名
registerIdentitytest IR Agent発行者またはプラットフォームの支援
mint / burntest Token Agent発行者またはプラットフォームの支援
transferToken 保有者両当事者が適格性とルールを満たす

通常の開発者は Agent 秘密鍵を必要としません。Agent 権限操作は対象資産の発行者が実行します。投資家操作には自分のウォレットを使用し、秘密鍵を送信しないでください。

公開 PRWA デモ資産は現在 pause 中です。通常 transfer は Token Agent が unpause() を呼び出した後にのみ実行できます。

transfer の事前検査と送信

const recipient = '0xfc996C5378fe0cd03Fd4296075abc07B16b2B12F';
const senderAddress = await signer.getAddress();
const amount = ethers.utils.parseUnits('10', decimals);
if (await token.paused()) {
throw new Error('Token transfers are currently paused');
}
if (!await ir.isVerified(recipient)) {
throw new Error('Recipient is not verified for this asset');
}
const compliance = new ethers.Contract(
await token.compliance(),
['function canTransfer(address,address,uint256) view returns (bool)'],
provider
);
if (!await compliance.canTransfer(senderAddress, recipient, amount)) {
throw new Error('Transfer rejected by compliance rules');
}
const receipt =
await (await token.connect(signer).transfer(recipient, amount)).wait();
console.log(receipt.transactionHash);

approve + transferFrom

await (await token.connect(ownerSigner).approve(spender, amount)).wait();
await (await token.connect(spenderSigner)
.transferFrom(ownerAddress, recipient, amount)).wait();

事前検査後も状態は変化し得るため、レシートが最終結果です。

6.10 ローンチ受入チェックリスト

環境

  • Testnet と Mainnet の設定が独立している。
  • 起動時に Chain ID を厳密に検証する。
  • RPC、Explorer、コントラクトアドレスは公式リリース情報を使用する。
  • Mainnet 設定が Testnet アドレスへ自動 fallback しない。

コントラクト読み取り

  • Token の名称、シンボル、decimals が正しい。
  • Token に紐づく IR と MC がアドレス表に一致する。
  • アドレスにコントラクト bytecode がある。
  • ABI がデプロイ済みバージョンと一致する。

金額とトランザクション

  • 全金額で parseUnits / formatUnits を使用する。
  • write 操作で submitted、confirmed、failed を区別する。
  • tx hash が正しい Explorer を指す。
  • 失敗時に最終業務状態を更新しない。
  • retry が mint または burn を重複送信しない。

ID とコンプライアンス

  • 対象資産の IR で適格性を照会する。
  • containsisVerified を区別する。
  • 複数資産で誤った IR を再利用しない。
  • KYC 平文はオフチェーンに保持する。
  • コンプライアンス拒否理由を明確に表示する。

セキュリティ

  • 秘密鍵をフロントエンド、リポジトリ、ログに置かない。
  • Agent と Claim Signer は管理された署名方式を使う。
  • プロキシアドレスのみを使用する。
  • アップグレードとセキュリティ通知を購読する。

6.11 トラブルシューティング順序

1. Chain ID を確認
2. RPC の利用可否を確認
3. 現在のネットワークでアドレスに bytecode があるか確認
4. Token.name / decimals / identityRegistry を読み取り
5. balance / paused / isFrozen を照会
6. 受取人 contains / isVerified を照会
7. compliance.canTransfer を照会
8. 署名者ロールを確認
9. callStatic / estimateGas で revert 理由を取得
10. 送信後にレシートとイベントを照会

Testnet で成功し Mainnet で失敗する場合は、ネットワーク、コントラクトアドレス、資産適格要件、コンプライアンスルールを比較してください。同一設定と仮定してはいけません。

前へ:05 · コントラクトインターフェースとイベント · 01 · プロジェクトと統合の概要へ