sax.delta
1 // Copyright (c) 2025 ALPHA/DELTA Network 2 // This file is part of the DeltaVM library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /**********************************************************************************************************************/ 17 // DELTA sAX (Synthetic AX) Program 18 // 19 // sAX is a synthetic representation of AX locked on the ALPHA chain. 20 // It enables trading on DELTA while maintaining 1:1 backing with locked AX. 21 // 22 // Properties: 23 // - 1:1 backing with Locked AX Pool on ALPHA 24 // - Ephemeral (exists only while AX is locked) 25 // - 4 decimals (matches AX: 1 AX = 10,000 microcredits) 26 // - Non-mintable except through verified lock attestations 27 // - Burnable only through unlock requests 28 // 29 // Cross-Chain Flow: 30 // Lock (AX → sAX): 31 // 1. User locks AX on ALPHA via alpha.credits::lock_for_sax() 32 // 2. ALPHA block finalizes (3 blocks) 33 // 3. IPC sends LockAttestation to DELTA runtime 34 // 4. This program mints sAX upon verified attestation 35 // 36 // Unlock (sAX → AX): 37 // 1. User calls burn_for_ax() on DELTA 38 // 2. sAX is burned, unlock record created 39 // 3. DELTA block finalizes (3 blocks) 40 // 4. IPC sends UnlockAttestation to ALPHA runtime 41 // 5. ALPHA releases AX from Locked Pool 42 /**********************************************************************************************************************/ 43 44 program delta.sax; 45 46 /**********************************************************************************************************************/ 47 // CONSTANTS (represented as literals in code) 48 /**********************************************************************************************************************/ 49 50 // FINALITY_BLOCKS = 3 51 // DECIMALS = 4 (1 sAX = 10,000 microcredits) 52 // MIN_LOCK_AMOUNT = 10000 (1 sAX minimum) 53 // MAX_PENDING_LOCKS = 1000 (per user) 54 55 /**********************************************************************************************************************/ 56 // STRUCTS 57 /**********************************************************************************************************************/ 58 59 // sAX balance record. 60 struct sax_balance: 61 // Owner address. 62 owner as address; 63 // Total balance in microcredits. 64 balance as u128; 65 // Amount pending unlock. 66 pending_unlock as u128; 67 // Last updated block. 68 updated_at as u32; 69 70 // Lock attestation from ALPHA chain. 71 struct lock_attestation: 72 // Lock ID (unique, from ALPHA). 73 lock_id as u128; 74 // User who locked AX. 75 user as address; 76 // Amount locked in microcredits. 77 amount as u128; 78 // ALPHA block where lock occurred. 79 alpha_block as u64; 80 // Merkle root of ALPHA state. 81 merkle_root as field; 82 // Status: 0 = pending, 1 = processed, 2 = failed. 83 status as u8; 84 // DELTA block when processed. 85 processed_at as u32; 86 87 // Unlock request for AX release. 88 struct unlock_request: 89 // Unlock ID (unique). 90 unlock_id as u128; 91 // User requesting unlock. 92 user as address; 93 // Amount to unlock in microcredits. 94 amount as u128; 95 // DELTA block when requested. 96 requested_at as u32; 97 // Status: 0 = pending, 1 = finalized, 2 = completed. 98 status as u8; 99 // DELTA block when finalized. 100 finalized_at as u32; 101 102 // Pending lock for user. 103 struct pending_lock: 104 // Lock ID. 105 lock_id as u128; 106 // Amount. 107 amount as u128; 108 // ALPHA block. 109 alpha_block as u64; 110 111 // Cross-chain statistics. 112 struct cross_chain_stats: 113 // Total AX locked (on ALPHA side). 114 total_locked as u128; 115 // Total sAX in circulation. 116 total_sax as u128; 117 // Total lock operations. 118 lock_count as u64; 119 // Total unlock operations. 120 unlock_count as u64; 121 // Last sync block. 122 last_sync_block as u32; 123 124 // Program configuration. 125 struct sax_config: 126 // Minimum lock amount. 127 min_lock_amount as u128; 128 // Finality blocks required. 129 finality_blocks as u32; 130 // Whether minting is paused. 131 mint_paused as boolean; 132 // Whether burning is paused. 133 burn_paused as boolean; 134 // Admin address. 135 admin as address; 136 // System address (for IPC attestations). 137 system as address; 138 139 // Transfer record. 140 struct transfer_record: 141 // Transfer ID. 142 transfer_id as u64; 143 // From address. 144 from as address; 145 // To address. 146 to as address; 147 // Amount. 148 amount as u128; 149 // Block when transferred. 150 transferred_at as u32; 151 152 /**********************************************************************************************************************/ 153 // MAPPINGS 154 /**********************************************************************************************************************/ 155 156 // User balances. 157 mapping balances: 158 key as address.public; 159 value as sax_balance.public; 160 161 // Lock attestations by ID. 162 mapping lock_attestations: 163 key as u128.public; 164 value as lock_attestation.public; 165 166 // Unlock requests by ID. 167 mapping unlock_requests: 168 key as u128.public; 169 value as unlock_request.public; 170 171 // Lock ID counter. 172 mapping lock_counter: 173 key as u8.public; 174 value as u128.public; 175 176 // Unlock ID counter. 177 mapping unlock_counter: 178 key as u8.public; 179 value as u128.public; 180 181 // Transfer counter. 182 mapping transfer_counter: 183 key as u8.public; 184 value as u64.public; 185 186 // Transfer records. 187 mapping transfers: 188 key as u64.public; 189 value as transfer_record.public; 190 191 // Cross-chain statistics. 192 mapping stats: 193 key as u8.public; 194 value as cross_chain_stats.public; 195 196 // Program configuration. 197 mapping config: 198 key as u8.public; 199 value as sax_config.public; 200 201 // Processed lock IDs (to prevent replay). 202 mapping processed_locks: 203 key as u128.public; 204 value as boolean.public; 205 206 // User pending unlock amounts. 207 mapping pending_unlocks: 208 key as address.public; 209 value as u128.public; 210 211 // Allowances for delegated transfers. 212 mapping allowances: 213 key as field.public; // hash(owner, spender) 214 value as u128.public; 215 216 // Total supply. 217 mapping total_supply: 218 key as u8.public; 219 value as u128.public; 220 221 /**********************************************************************************************************************/ 222 // FUNCTIONS 223 /**********************************************************************************************************************/ 224 225 // Initialize the sAX program. 226 function initialize: 227 input r0 as address.public; // Admin address. 228 input r1 as address.public; // System address (for IPC). 229 230 finalize initialize r0 r1 self.caller; 231 232 finalize initialize: 233 input r0 as address.public; 234 input r1 as address.public; 235 input r2 as address.public; 236 237 // Ensure not already initialized. 238 get.or_use config[0u8] sax_config { 239 min_lock_amount: 0u128, 240 finality_blocks: 0u32, 241 mint_paused: false, 242 burn_paused: false, 243 admin: r0, 244 system: r1 245 } into r3; 246 assert.eq r3.min_lock_amount 0u128; 247 248 // Create configuration. 249 cast 10000u128 3u32 false false r0 r1 into r4 as sax_config; 250 set r4 into config[0u8]; 251 252 // Initialize counters. 253 set 0u128 into lock_counter[0u8]; 254 set 0u128 into unlock_counter[0u8]; 255 set 0u64 into transfer_counter[0u8]; 256 set 0u128 into total_supply[0u8]; 257 258 // Initialize stats. 259 cast 0u128 0u128 0u64 0u64 0u32 into r5 as cross_chain_stats; 260 set r5 into stats[0u8]; 261 262 // Process lock attestation from ALPHA chain (system only). 263 // Called by adnet runtime when LockAttestation IPC message is received. 264 function process_lock: 265 input r0 as u128.public; // Lock ID from ALPHA. 266 input r1 as address.public; // User address. 267 input r2 as u128.public; // Amount in microcredits. 268 input r3 as u64.public; // ALPHA block number. 269 input r4 as field.public; // Merkle root (for verification). 270 271 finalize process_lock r0 r1 r2 r3 r4 self.caller; 272 273 finalize process_lock: 274 input r0 as u128.public; 275 input r1 as address.public; 276 input r2 as u128.public; 277 input r3 as u64.public; 278 input r4 as field.public; 279 input r5 as address.public; 280 281 // Verify caller is system. 282 get config[0u8] into r6; 283 assert.eq r5 r6.system; 284 285 // Check minting is not paused. 286 assert.eq r6.mint_paused false; 287 288 // Verify amount meets minimum. 289 gte r2 r6.min_lock_amount into r7; 290 assert.eq r7 true; 291 292 // Ensure lock hasn't been processed (prevent replay). 293 get.or_use processed_locks[r0] false into r8; 294 assert.eq r8 false; 295 296 // Mark lock as processed. 297 set true into processed_locks[r0]; 298 299 // Create lock attestation record. 300 cast r0 r1 r2 r3 r4 1u8 block.height into r9 as lock_attestation; 301 set r9 into lock_attestations[r0]; 302 303 // Mint sAX to user. 304 get.or_use balances[r1] sax_balance { 305 owner: r1, 306 balance: 0u128, 307 pending_unlock: 0u128, 308 updated_at: 0u32 309 } into r10; 310 311 add r10.balance r2 into r11; 312 cast r1 r11 r10.pending_unlock block.height into r12 as sax_balance; 313 set r12 into balances[r1]; 314 315 // Update total supply. 316 get.or_use total_supply[0u8] 0u128 into r13; 317 add r13 r2 into r14; 318 set r14 into total_supply[0u8]; 319 320 // Update stats. 321 get.or_use stats[0u8] cross_chain_stats { 322 total_locked: 0u128, 323 total_sax: 0u128, 324 lock_count: 0u64, 325 unlock_count: 0u64, 326 last_sync_block: 0u32 327 } into r15; 328 329 add r15.total_locked r2 into r16; 330 add r15.total_sax r2 into r17; 331 add r15.lock_count 1u64 into r18; 332 cast r16 r17 r18 r15.unlock_count block.height into r19 as cross_chain_stats; 333 set r19 into stats[0u8]; 334 335 // Request to burn sAX and unlock AX on ALPHA. 336 function burn_for_ax: 337 input r0 as u128.public; // Amount to burn. 338 339 finalize burn_for_ax r0 self.caller; 340 341 finalize burn_for_ax: 342 input r0 as u128.public; 343 input r1 as address.public; 344 345 // Get config. 346 get config[0u8] into r2; 347 348 // Check burning is not paused. 349 assert.eq r2.burn_paused false; 350 351 // Verify amount meets minimum. 352 gte r0 r2.min_lock_amount into r3; 353 assert.eq r3 true; 354 355 // Get user balance. 356 get balances[r1] into r4; 357 358 // Verify sufficient balance. 359 gte r4.balance r0 into r5; 360 assert.eq r5 true; 361 362 // Deduct from balance and add to pending unlock. 363 sub r4.balance r0 into r6; 364 add r4.pending_unlock r0 into r7; 365 cast r1 r6 r7 block.height into r8 as sax_balance; 366 set r8 into balances[r1]; 367 368 // Create unlock request. 369 get.or_use unlock_counter[0u8] 0u128 into r9; 370 add r9 1u128 into r10; 371 set r10 into unlock_counter[0u8]; 372 373 cast r10 r1 r0 block.height 0u8 0u32 into r11 as unlock_request; 374 set r11 into unlock_requests[r10]; 375 376 // Update pending unlocks for user. 377 get.or_use pending_unlocks[r1] 0u128 into r12; 378 add r12 r0 into r13; 379 set r13 into pending_unlocks[r1]; 380 381 // Note: IPC will send UnlockAttestation to ALPHA after finality. 382 383 // Finalize unlock (called by system after DELTA finality). 384 function finalize_unlock: 385 input r0 as u128.public; // Unlock ID. 386 387 finalize finalize_unlock r0 self.caller; 388 389 finalize finalize_unlock: 390 input r0 as u128.public; 391 input r1 as address.public; 392 393 // Verify caller is system. 394 get config[0u8] into r2; 395 assert.eq r1 r2.system; 396 397 // Get unlock request. 398 get unlock_requests[r0] into r3; 399 400 // Must be pending. 401 assert.eq r3.status 0u8; 402 403 // Verify finality (3 blocks). 404 add r3.requested_at r2.finality_blocks into r4; 405 gte block.height r4 into r5; 406 assert.eq r5 true; 407 408 // Update status to finalized. 409 cast r3.unlock_id r3.user r3.amount r3.requested_at 1u8 block.height into r6 as unlock_request; 410 set r6 into unlock_requests[r0]; 411 412 // Reduce total supply (sAX effectively burned). 413 get total_supply[0u8] into r7; 414 sub r7 r3.amount into r8; 415 set r8 into total_supply[0u8]; 416 417 // Update stats. 418 get stats[0u8] into r9; 419 sub r9.total_sax r3.amount into r10; 420 add r9.unlock_count 1u64 into r11; 421 cast r9.total_locked r10 r9.lock_count r11 block.height into r12 as cross_chain_stats; 422 set r12 into stats[0u8]; 423 424 // Clear user's pending unlock. 425 get pending_unlocks[r3.user] into r13; 426 sub r13 r3.amount into r14; 427 set r14 into pending_unlocks[r3.user]; 428 429 // Clear pending from balance. 430 get balances[r3.user] into r15; 431 sub r15.pending_unlock r3.amount into r16; 432 cast r3.user r15.balance r16 block.height into r17 as sax_balance; 433 set r17 into balances[r3.user]; 434 435 // Mark unlock as completed (after AX released on ALPHA). 436 function complete_unlock: 437 input r0 as u128.public; // Unlock ID. 438 439 finalize complete_unlock r0 self.caller; 440 441 finalize complete_unlock: 442 input r0 as u128.public; 443 input r1 as address.public; 444 445 // Verify caller is system. 446 get config[0u8] into r2; 447 assert.eq r1 r2.system; 448 449 // Get unlock request. 450 get unlock_requests[r0] into r3; 451 452 // Must be finalized. 453 assert.eq r3.status 1u8; 454 455 // Update status to completed. 456 cast r3.unlock_id r3.user r3.amount r3.requested_at 2u8 r3.finalized_at into r4 as unlock_request; 457 set r4 into unlock_requests[r0]; 458 459 // Reduce total locked (AX released on ALPHA side). 460 get stats[0u8] into r5; 461 sub r5.total_locked r3.amount into r6; 462 cast r6 r5.total_sax r5.lock_count r5.unlock_count r5.last_sync_block into r7 as cross_chain_stats; 463 set r7 into stats[0u8]; 464 465 // Transfer sAX to another address. 466 function transfer: 467 input r0 as address.public; // To address. 468 input r1 as u128.public; // Amount. 469 470 finalize transfer r0 r1 self.caller; 471 472 finalize transfer: 473 input r0 as address.public; 474 input r1 as u128.public; 475 input r2 as address.public; 476 477 // Get sender balance. 478 get balances[r2] into r3; 479 480 // Verify sufficient balance (excluding pending unlock). 481 sub r3.balance r3.pending_unlock into r4; 482 gte r4 r1 into r5; 483 assert.eq r5 true; 484 485 // Deduct from sender. 486 sub r3.balance r1 into r6; 487 cast r2 r6 r3.pending_unlock block.height into r7 as sax_balance; 488 set r7 into balances[r2]; 489 490 // Add to recipient. 491 get.or_use balances[r0] sax_balance { 492 owner: r0, 493 balance: 0u128, 494 pending_unlock: 0u128, 495 updated_at: 0u32 496 } into r8; 497 498 add r8.balance r1 into r9; 499 cast r0 r9 r8.pending_unlock block.height into r10 as sax_balance; 500 set r10 into balances[r0]; 501 502 // Create transfer record. 503 get transfer_counter[0u8] into r11; 504 add r11 1u64 into r12; 505 set r12 into transfer_counter[0u8]; 506 507 cast r12 r2 r0 r1 block.height into r13 as transfer_record; 508 set r13 into transfers[r12]; 509 510 // Approve allowance for delegated transfer. 511 function approve: 512 input r0 as address.public; // Spender. 513 input r1 as u128.public; // Amount. 514 515 finalize approve r0 r1 self.caller; 516 517 finalize approve: 518 input r0 as address.public; 519 input r1 as u128.public; 520 input r2 as address.public; 521 522 // Create allowance key: hash(owner, spender). 523 hash.bhp256 r2 into r3 as field; 524 hash.bhp256 r0 into r4 as field; 525 add r3 r4 into r5; 526 527 set r1 into allowances[r5]; 528 529 // Transfer from allowance. 530 function transfer_from: 531 input r0 as address.public; // From address. 532 input r1 as address.public; // To address. 533 input r2 as u128.public; // Amount. 534 535 finalize transfer_from r0 r1 r2 self.caller; 536 537 finalize transfer_from: 538 input r0 as address.public; 539 input r1 as address.public; 540 input r2 as u128.public; 541 input r3 as address.public; 542 543 // Check allowance. 544 hash.bhp256 r0 into r4 as field; 545 hash.bhp256 r3 into r5 as field; 546 add r4 r5 into r6; 547 548 get allowances[r6] into r7; 549 gte r7 r2 into r8; 550 assert.eq r8 true; 551 552 // Deduct allowance. 553 sub r7 r2 into r9; 554 set r9 into allowances[r6]; 555 556 // Get from balance. 557 get balances[r0] into r10; 558 559 // Verify sufficient balance. 560 sub r10.balance r10.pending_unlock into r11; 561 gte r11 r2 into r12; 562 assert.eq r12 true; 563 564 // Deduct from sender. 565 sub r10.balance r2 into r13; 566 cast r0 r13 r10.pending_unlock block.height into r14 as sax_balance; 567 set r14 into balances[r0]; 568 569 // Add to recipient. 570 get.or_use balances[r1] sax_balance { 571 owner: r1, 572 balance: 0u128, 573 pending_unlock: 0u128, 574 updated_at: 0u32 575 } into r15; 576 577 add r15.balance r2 into r16; 578 cast r1 r16 r15.pending_unlock block.height into r17 as sax_balance; 579 set r17 into balances[r1]; 580 581 // Pause minting (admin only, emergency). 582 function pause_mint: 583 input r0 as boolean.public; // Pause state. 584 585 finalize pause_mint r0 self.caller; 586 587 finalize pause_mint: 588 input r0 as boolean.public; 589 input r1 as address.public; 590 591 // Verify admin. 592 get config[0u8] into r2; 593 assert.eq r1 r2.admin; 594 595 // Update config. 596 cast r2.min_lock_amount r2.finality_blocks r0 r2.burn_paused r2.admin r2.system into r3 as sax_config; 597 set r3 into config[0u8]; 598 599 // Pause burning (admin only, emergency). 600 function pause_burn: 601 input r0 as boolean.public; // Pause state. 602 603 finalize pause_burn r0 self.caller; 604 605 finalize pause_burn: 606 input r0 as boolean.public; 607 input r1 as address.public; 608 609 // Verify admin. 610 get config[0u8] into r2; 611 assert.eq r1 r2.admin; 612 613 // Update config. 614 cast r2.min_lock_amount r2.finality_blocks r2.mint_paused r0 r2.admin r2.system into r3 as sax_config; 615 set r3 into config[0u8]; 616 617 // Get balance of an address. 618 function balance_of: 619 input r0 as address.public; 620 621 finalize balance_of r0; 622 623 finalize balance_of: 624 input r0 as address.public; 625 626 // Read-only operation. 627 get.or_use balances[r0] sax_balance { 628 owner: r0, 629 balance: 0u128, 630 pending_unlock: 0u128, 631 updated_at: 0u32 632 } into r1; 633 634 // Get total supply. 635 function get_total_supply: 636 finalize get_total_supply; 637 638 finalize get_total_supply: 639 // Read-only operation. 640 get total_supply[0u8] into r0; 641 642 // Get cross-chain statistics. 643 function get_stats: 644 finalize get_stats; 645 646 finalize get_stats: 647 // Read-only operation. 648 get stats[0u8] into r0;