Transaction Hash:
Block:
23988767 at Dec-11-2025 10:13:11 AM +UTC
Transaction Fee:
0.000041090403313875 ETH
$0.12
Gas Used:
186,099 Gas / 0.220798625 Gwei
Emitted Events:
| 211 |
TetherToken.Transfer( from=[Sender] 0x642cef49891994509a3f5edcd3a37c0e9a80f8e4, to=0x217e42CEB2eAE9ECB788fDF0e31c806c531760A3, value=20938575123 )
|
| 212 |
Vat.Slip( ilk=50534D2D555344542D4100000000000000000000000000000000000000000000, usr=[Receiver] 0xce355440c00014a229bbec030a2b8f8eb45a2897, wad=20938575123000000000000 )
|
| 213 |
0x217e42ceb2eae9ecb788fdf0e31c806c531760a3.0x16c03c2fe01ac285473b0d10ba5c5de59ede582fcac27a866b5827415fe44b03( 0x16c03c2fe01ac285473b0d10ba5c5de59ede582fcac27a866b5827415fe44b03, 0x000000000000000000000000ce355440c00014a229bbec030a2b8f8eb45a2897, 0x000000000000000000000000642cef49891994509a3f5edcd3a37c0e9a80f8e4, 00000000000000000000000000000000000000000000000000000004e0094d13 )
|
| 214 |
Vat.Frob( ilk=50534D2D555344542D4100000000000000000000000000000000000000000000, urn=[Receiver] 0xce355440c00014a229bbec030a2b8f8eb45a2897, v=[Receiver] 0xce355440c00014a229bbec030a2b8f8eb45a2897, w=[Receiver] 0xce355440c00014a229bbec030a2b8f8eb45a2897, dink=20938575123000000000000, dart=20938575123000000000000 )
|
| 215 |
Vat.Move( src=[Receiver] 0xce355440c00014a229bbec030a2b8f8eb45a2897, dst=0xf085EDD7...113d06f3B, rad=0 )
|
| 216 |
Vat.Move( src=[Receiver] 0xce355440c00014a229bbec030a2b8f8eb45a2897, dst=UsddJoin, rad=20938575123000000000000000000000000000000000000000 )
|
| 217 |
Usdd.Transfer( src=0x0000000000000000000000000000000000000000, dst=[Sender] 0x642cef49891994509a3f5edcd3a37c0e9a80f8e4, wad=20938575123000000000000 )
|
| 218 |
UsddJoin.Exit( usr=[Sender] 0x642cef49891994509a3f5edcd3a37c0e9a80f8e4, wad=20938575123000000000000 )
|
| 219 |
0xce355440c00014a229bbec030a2b8f8eb45a2897.0xef75f5a47cc9a929968796ceb84f19e7541617b4577f2c228ea95200e1572081( 0xef75f5a47cc9a929968796ceb84f19e7541617b4577f2c228ea95200e1572081, 0x000000000000000000000000642cef49891994509a3f5edcd3a37c0e9a80f8e4, 00000000000000000000000000000000000000000000000000000004e0094d13, 0000000000000000000000000000000000000000000000000000000000000000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 20.964106988549118254 Eth | 20.964123551360118254 Eth | 0.000016562811 | |
| 0x4f8e5DE4...461beCD1A | |||||
| 0x642cef49...E9a80f8E4 |
0.007971657233566801 Eth
Nonce: 63
|
0.007930566830252926 Eth
Nonce: 64
| 0.000041090403313875 | ||
| 0xdAC17F95...13D831ec7 | |||||
| 0xFf77F620...b0032097f |
Execution Trace
0xce355440c00014a229bbec030a2b8f8eb45a2897.95991276( )
0x217e42ceb2eae9ecb788fdf0e31c806c531760a3.d14b1e4b( )-
TetherToken.STATICCALL( )
-
TetherToken.balanceOf( who=0x217e42CEB2eAE9ECB788fDF0e31c806c531760A3 ) => ( 100033228134232 )
-
TetherToken.transferFrom( _from=0x642cef49891994509a3f5eDCD3a37C0E9a80f8E4, _to=0x217e42CEB2eAE9ECB788fDF0e31c806c531760A3, _value=20938575123 )
-
TetherToken.balanceOf( who=0x217e42CEB2eAE9ECB788fDF0e31c806c531760A3 ) => ( 100054166709355 )
-
Vat.slip( ilk=50534D2D555344542D4100000000000000000000000000000000000000000000, usr=0xcE355440c00014A229bbEc030A2B8f8EB45a2897, wad=20938575123000000000000 )
-
-
Vat.frob( i=50534D2D555344542D4100000000000000000000000000000000000000000000, u=0xcE355440c00014A229bbEc030A2B8f8EB45a2897, v=0xcE355440c00014A229bbEc030A2B8f8EB45a2897, w=0xcE355440c00014A229bbEc030A2B8f8EB45a2897, dink=20938575123000000000000, dart=20938575123000000000000 ) -
Vat.move( src=0xcE355440c00014A229bbEc030A2B8f8EB45a2897, dst=0xf085EDD75c1AB4fdA0C3Bd49b264A4a113d06f3B, rad=0 ) UsddJoin.exit( usr=0x642cef49891994509a3f5eDCD3a37C0E9a80f8E4, wad=20938575123000000000000 )
File 1 of 4: TetherToken
File 2 of 4: Vat
File 3 of 4: Usdd
File 4 of 4: UsddJoin
pragma solidity ^0.4.17;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20Basic {
uint public _totalSupply;
function totalSupply() public constant returns (uint);
function balanceOf(address who) public constant returns (uint);
function transfer(address to, uint value) public;
event Transfer(address indexed from, address indexed to, uint value);
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public constant returns (uint);
function transferFrom(address from, address to, uint value) public;
function approve(address spender, uint value) public;
event Approval(address indexed owner, address indexed spender, uint value);
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is Ownable, ERC20Basic {
using SafeMath for uint;
mapping(address => uint) public balances;
// additional variables for use if transaction fees ever became necessary
uint public basisPointsRate = 0;
uint public maximumFee = 0;
/**
* @dev Fix for the ERC20 short address attack.
*/
modifier onlyPayloadSize(uint size) {
require(!(msg.data.length < size + 4));
_;
}
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
uint sendAmount = _value.sub(fee);
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(msg.sender, owner, fee);
}
Transfer(msg.sender, _to, sendAmount);
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public constant returns (uint balance) {
return balances[_owner];
}
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is BasicToken, ERC20 {
mapping (address => mapping (address => uint)) public allowed;
uint public constant MAX_UINT = 2**256 - 1;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
var _allowance = allowed[_from][msg.sender];
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// if (_value > _allowance) throw;
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
if (_allowance < MAX_UINT) {
allowed[_from][msg.sender] = _allowance.sub(_value);
}
uint sendAmount = _value.sub(fee);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(_from, owner, fee);
}
Transfer(_from, _to, sendAmount);
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender, 0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
}
/**
* @dev Function to check the amount of tokens than an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
return allowed[_owner][_spender];
}
}
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
contract BlackList is Ownable, BasicToken {
/////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
function getBlackListStatus(address _maker) external constant returns (bool) {
return isBlackListed[_maker];
}
function getOwner() external constant returns (address) {
return owner;
}
mapping (address => bool) public isBlackListed;
function addBlackList (address _evilUser) public onlyOwner {
isBlackListed[_evilUser] = true;
AddedBlackList(_evilUser);
}
function removeBlackList (address _clearedUser) public onlyOwner {
isBlackListed[_clearedUser] = false;
RemovedBlackList(_clearedUser);
}
function destroyBlackFunds (address _blackListedUser) public onlyOwner {
require(isBlackListed[_blackListedUser]);
uint dirtyFunds = balanceOf(_blackListedUser);
balances[_blackListedUser] = 0;
_totalSupply -= dirtyFunds;
DestroyedBlackFunds(_blackListedUser, dirtyFunds);
}
event DestroyedBlackFunds(address _blackListedUser, uint _balance);
event AddedBlackList(address _user);
event RemovedBlackList(address _user);
}
contract UpgradedStandardToken is StandardToken{
// those methods are called by the legacy contract
// and they must ensure msg.sender to be the contract address
function transferByLegacy(address from, address to, uint value) public;
function transferFromByLegacy(address sender, address from, address spender, uint value) public;
function approveByLegacy(address from, address spender, uint value) public;
}
contract TetherToken is Pausable, StandardToken, BlackList {
string public name;
string public symbol;
uint public decimals;
address public upgradedAddress;
bool public deprecated;
// The contract can be initialized with a number of tokens
// All the tokens are deposited to the owner address
//
// @param _balance Initial supply of the contract
// @param _name Token Name
// @param _symbol Token symbol
// @param _decimals Token decimals
function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
_totalSupply = _initialSupply;
name = _name;
symbol = _symbol;
decimals = _decimals;
balances[owner] = _initialSupply;
deprecated = false;
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function transfer(address _to, uint _value) public whenNotPaused {
require(!isBlackListed[msg.sender]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
} else {
return super.transfer(_to, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
require(!isBlackListed[_from]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
} else {
return super.transferFrom(_from, _to, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function balanceOf(address who) public constant returns (uint) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).balanceOf(who);
} else {
return super.balanceOf(who);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
} else {
return super.approve(_spender, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
if (deprecated) {
return StandardToken(upgradedAddress).allowance(_owner, _spender);
} else {
return super.allowance(_owner, _spender);
}
}
// deprecate current contract in favour of a new one
function deprecate(address _upgradedAddress) public onlyOwner {
deprecated = true;
upgradedAddress = _upgradedAddress;
Deprecate(_upgradedAddress);
}
// deprecate current contract if favour of a new one
function totalSupply() public constant returns (uint) {
if (deprecated) {
return StandardToken(upgradedAddress).totalSupply();
} else {
return _totalSupply;
}
}
// Issue a new amount of tokens
// these tokens are deposited into the owner address
//
// @param _amount Number of tokens to be issued
function issue(uint amount) public onlyOwner {
require(_totalSupply + amount > _totalSupply);
require(balances[owner] + amount > balances[owner]);
balances[owner] += amount;
_totalSupply += amount;
Issue(amount);
}
// Redeem tokens.
// These tokens are withdrawn from the owner address
// if the balance must be enough to cover the redeem
// or the call will fail.
// @param _amount Number of tokens to be issued
function redeem(uint amount) public onlyOwner {
require(_totalSupply >= amount);
require(balances[owner] >= amount);
_totalSupply -= amount;
balances[owner] -= amount;
Redeem(amount);
}
function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
// Ensure transparency by hardcoding limit beyond which fees can never be added
require(newBasisPoints < 20);
require(newMaxFee < 50);
basisPointsRate = newBasisPoints;
maximumFee = newMaxFee.mul(10**decimals);
Params(basisPointsRate, maximumFee);
}
// Called when new token are issued
event Issue(uint amount);
// Called when tokens are redeemed
event Redeem(uint amount);
// Called when contract is deprecated
event Deprecate(address newAddress);
// Called if contract ever adds fees
event Params(uint feeBasisPoints, uint maxFee);
}File 2 of 4: Vat
// SPDX-License-Identifier: AGPL-3.0-or-later /// vat.sol -- Usdd CDP database // Copyright (C) 2018 Rain <[email protected]> // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.6.12; contract Vat { // --- Events --- event Rely(address usr); event Deny(address usr); event Hope(address usr); event Nope(address usr); event Init(bytes32 ilk); event File(bytes32 what, uint data); event File(bytes32 ilk, bytes32 what, uint data); event Cage(); event Slip(bytes32 ilk, address usr, int256 wad); event Flux(bytes32 ilk, address src, address dst, uint256 wad); event Move(address src, address dst, uint256 rad); event Frob(bytes32 ilk, address urn, address v, address w, int dink, int dart); event Fork(bytes32 ilk, address src, address dst, int dink, int dart); event Grab(bytes32 i, address u, address v, address w, int dink, int dart); event Heal(uint rad); event Suck(address u, address v, uint rad); event Fold(bytes32 i, address u, int rate); // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external auth { require(live == 1, "Vat/not-live"); wards[usr] = 1; emit Rely(usr); } function deny(address usr) external auth { require(live == 1, "Vat/not-live"); wards[usr] = 0; emit Deny(usr); } modifier auth { require(wards[msg.sender] == 1, "Vat/not-authorized"); _; } mapping(address => mapping (address => uint)) public can; function hope(address usr) external { can[msg.sender][usr] = 1; emit Hope(usr); } function nope(address usr) external { can[msg.sender][usr] = 0; emit Nope(usr); } function wish(address bit, address usr) internal view returns (bool) { return either(bit == usr, can[bit][usr] == 1); } // --- Data --- struct Ilk { uint256 Art; // Total Normalised Debt [wad] uint256 rate; // Accumulated Rates [ray] uint256 spot; // Price with Safety Margin [ray] uint256 line; // Debt Ceiling [rad] uint256 dust; // Urn Debt Floor [rad] } struct Urn { uint256 ink; // Locked Collateral [wad] uint256 art; // Normalised Debt [wad] } mapping (bytes32 => Ilk) public ilks; mapping (bytes32 => mapping (address => Urn )) public urns; mapping (bytes32 => mapping (address => uint)) public gem; // [wad] mapping (address => uint256) public usdd; // [rad] mapping (address => uint256) public sin; // [rad] uint256 public debt; // Total Usdd Issued [rad] uint256 public vice; // Total Unbacked Usdd [rad] uint256 public Line; // Total Debt Ceiling [rad] uint256 public live; // Active Flag // --- Init --- constructor() public { wards[msg.sender] = 1; live = 1; } // --- Math --- function _add(uint x, int y) internal pure returns (uint z) { z = x + uint(y); require(y >= 0 || z <= x); require(y <= 0 || z >= x); } function _sub(uint x, int y) internal pure returns (uint z) { z = x - uint(y); require(y <= 0 || z <= x); require(y >= 0 || z >= x); } function _mul(uint x, int y) internal pure returns (int z) { z = int(x) * y; require(int(x) >= 0); require(y == 0 || z / y == int(x)); } function _add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x); } function _sub(uint x, uint y) internal pure returns (uint z) { require((z = x - y) <= x); } function _mul(uint x, uint y) internal pure returns (uint z) { require(y == 0 || (z = x * y) / y == x); } // --- Administration --- function init(bytes32 ilk) external auth { require(ilks[ilk].rate == 0, "Vat/ilk-already-init"); ilks[ilk].rate = 10 ** 27; emit Init(ilk); } function file(bytes32 what, uint data) external auth { require(live == 1, "Vat/not-live"); if (what == "Line") Line = data; else revert("Vat/file-unrecognized-param"); emit File(what, data); } function file(bytes32 ilk, bytes32 what, uint data) external auth { require(live == 1, "Vat/not-live"); if (what == "spot") ilks[ilk].spot = data; else if (what == "line") ilks[ilk].line = data; else if (what == "dust") ilks[ilk].dust = data; else revert("Vat/file-unrecognized-param"); emit File(ilk, what, data); } function cage() external auth { live = 0; emit Cage(); } // --- Fungibility --- function slip(bytes32 ilk, address usr, int256 wad) external auth { gem[ilk][usr] = _add(gem[ilk][usr], wad); emit Slip(ilk, usr, wad); } function flux(bytes32 ilk, address src, address dst, uint256 wad) external { require(wish(src, msg.sender), "Vat/not-allowed"); gem[ilk][src] = _sub(gem[ilk][src], wad); gem[ilk][dst] = _add(gem[ilk][dst], wad); emit Flux(ilk, src, dst, wad); } function move(address src, address dst, uint256 rad) external { require(wish(src, msg.sender), "Vat/not-allowed"); usdd[src] = _sub(usdd[src], rad); usdd[dst] = _add(usdd[dst], rad); emit Move(src, dst, rad); } function either(bool x, bool y) internal pure returns (bool z) { assembly{ z := or(x, y)} } function both(bool x, bool y) internal pure returns (bool z) { assembly{ z := and(x, y)} } // --- CDP Manipulation --- function frob(bytes32 i, address u, address v, address w, int dink, int dart) external { // system is live require(live == 1, "Vat/not-live"); Urn memory urn = urns[i][u]; Ilk memory ilk = ilks[i]; // ilk has been initialised require(ilk.rate != 0, "Vat/ilk-not-init"); urn.ink = _add(urn.ink, dink); urn.art = _add(urn.art, dart); ilk.Art = _add(ilk.Art, dart); int dtab = _mul(ilk.rate, dart); uint tab = _mul(ilk.rate, urn.art); debt = _add(debt, dtab); // either debt has decreased, or debt ceilings are not exceeded require(either(dart <= 0, both(_mul(ilk.Art, ilk.rate) <= ilk.line, debt <= Line)), "Vat/ceiling-exceeded"); // urn is either less risky than before, or it is safe require(either(both(dart <= 0, dink >= 0), tab <= _mul(urn.ink, ilk.spot)), "Vat/not-safe"); // urn is either more safe, or the owner consents require(either(both(dart <= 0, dink >= 0), wish(u, msg.sender)), "Vat/not-allowed-u"); // collateral src consents require(either(dink <= 0, wish(v, msg.sender)), "Vat/not-allowed-v"); // debt dst consents require(either(dart >= 0, wish(w, msg.sender)), "Vat/not-allowed-w"); // urn has no debt, or a non-dusty amount require(either(urn.art == 0, tab >= ilk.dust), "Vat/dust"); gem[i][v] = _sub(gem[i][v], dink); usdd[w] = _add(usdd[w], dtab); urns[i][u] = urn; ilks[i] = ilk; emit Frob(i, u, v, w, dink, dart); } // --- CDP Fungibility --- function fork(bytes32 ilk, address src, address dst, int dink, int dart) external { Urn storage u = urns[ilk][src]; Urn storage v = urns[ilk][dst]; Ilk storage i = ilks[ilk]; u.ink = _sub(u.ink, dink); u.art = _sub(u.art, dart); v.ink = _add(v.ink, dink); v.art = _add(v.art, dart); uint utab = _mul(u.art, i.rate); uint vtab = _mul(v.art, i.rate); // both sides consent require(both(wish(src, msg.sender), wish(dst, msg.sender)), "Vat/not-allowed"); // both sides safe require(utab <= _mul(u.ink, i.spot), "Vat/not-safe-src"); require(vtab <= _mul(v.ink, i.spot), "Vat/not-safe-dst"); // both sides non-dusty require(either(utab >= i.dust, u.art == 0), "Vat/dust-src"); require(either(vtab >= i.dust, v.art == 0), "Vat/dust-dst"); emit Fork(ilk, src, dst, dink, dart); } // --- CDP Confiscation --- function grab(bytes32 i, address u, address v, address w, int dink, int dart) external auth { Urn storage urn = urns[i][u]; Ilk storage ilk = ilks[i]; urn.ink = _add(urn.ink, dink); urn.art = _add(urn.art, dart); ilk.Art = _add(ilk.Art, dart); int dtab = _mul(ilk.rate, dart); gem[i][v] = _sub(gem[i][v], dink); sin[w] = _sub(sin[w], dtab); vice = _sub(vice, dtab); emit Grab(i, u, v, w, dink, dart); } // --- Settlement --- function heal(uint rad) external { address u = msg.sender; sin[u] = _sub(sin[u], rad); usdd[u] = _sub(usdd[u], rad); vice = _sub(vice, rad); debt = _sub(debt, rad); emit Heal(rad); } function suck(address u, address v, uint rad) external auth { sin[u] = _add(sin[u], rad); usdd[v] = _add(usdd[v], rad); vice = _add(vice, rad); debt = _add(debt, rad); emit Suck(u, v, rad); } // --- Rates --- function fold(bytes32 i, address u, int rate) external auth { require(live == 1, "Vat/not-live"); Ilk storage ilk = ilks[i]; ilk.rate = _add(ilk.rate, rate); int rad = _mul(ilk.Art, rate); usdd[u] = _add(usdd[u], rad); debt = _add(debt, rad); emit Fold(i, u, rate); } }
File 3 of 4: Usdd
// SPDX-License-Identifier: AGPL-3.0-or-later
/// usdd.sol -- Usdd Stablecoin ERC-20 Token
// Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.6.12;
contract Usdd {
// --- Auth ---
mapping (address => uint) public wards;
function rely(address guy) external auth { wards[guy] = 1; }
function deny(address guy) external auth { wards[guy] = 0; }
modifier auth {
require(wards[msg.sender] == 1, "Usdd/not-authorized");
_;
}
// --- ERC20 Data ---
string public constant name = "Usdd Stablecoin";
string public constant symbol = "USDD";
string public constant version = "1";
uint8 public constant decimals = 18;
uint256 public totalSupply;
mapping (address => uint) public balanceOf;
mapping (address => mapping (address => uint)) public allowance;
mapping (address => uint) public nonces;
event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
// --- Math ---
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x);
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x);
}
// --- EIP712 niceties ---
bytes32 public DOMAIN_SEPARATOR;
// bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
constructor(uint256 chainId_) public {
wards[msg.sender] = 1;
DOMAIN_SEPARATOR = keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes(version)),
chainId_,
address(this)
));
}
// --- Token ---
function transfer(address dst, uint wad) external returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
function transferFrom(address src, address dst, uint wad)
public returns (bool)
{
require(balanceOf[src] >= wad, "Usdd/insufficient-balance");
if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
require(allowance[src][msg.sender] >= wad, "Usdd/insufficient-allowance");
allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
}
balanceOf[src] = sub(balanceOf[src], wad);
balanceOf[dst] = add(balanceOf[dst], wad);
emit Transfer(src, dst, wad);
return true;
}
function mint(address usr, uint wad) external auth {
balanceOf[usr] = add(balanceOf[usr], wad);
totalSupply = add(totalSupply, wad);
emit Transfer(address(0), usr, wad);
}
function burn(address usr, uint wad) external {
require(balanceOf[usr] >= wad, "Usdd/insufficient-balance");
if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
require(allowance[usr][msg.sender] >= wad, "Usdd/insufficient-allowance");
allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
}
balanceOf[usr] = sub(balanceOf[usr], wad);
totalSupply = sub(totalSupply, wad);
emit Transfer(usr, address(0), wad);
}
function approve(address usr, uint wad) external returns (bool) {
allowance[msg.sender][usr] = wad;
emit Approval(msg.sender, usr, wad);
return true;
}
// --- Alias ---
function push(address usr, uint wad) external {
transferFrom(msg.sender, usr, wad);
}
function pull(address usr, uint wad) external {
transferFrom(usr, msg.sender, wad);
}
function move(address src, address dst, uint wad) external {
transferFrom(src, dst, wad);
}
// --- Approve by signature ---
function permit(address holder, address spender, uint256 nonce, uint256 expiry,
bool allowed, uint8 v, bytes32 r, bytes32 s) external
{
bytes32 digest =
keccak256(abi.encodePacked(
"\\x19\\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH,
holder,
spender,
nonce,
expiry,
allowed))
));
require(holder != address(0), "Usdd/invalid-address-0");
require(holder == ecrecover(digest, v, r, s), "Usdd/invalid-permit");
require(expiry == 0 || now <= expiry, "Usdd/permit-expired");
require(nonce == nonces[holder]++, "Usdd/invalid-nonce");
uint wad = allowed ? uint(-1) : 0;
allowance[holder][spender] = wad;
emit Approval(holder, spender, wad);
}
}
File 4 of 4: UsddJoin
// SPDX-License-Identifier: AGPL-3.0-or-later /// join.sol -- Basic token adapters // Copyright (C) 2018 Rain <[email protected]> // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.6.12; interface GemLike { function decimals() external view returns (uint); function transfer(address,uint) external returns (bool); function transferFrom(address,address,uint) external returns (bool); } interface DSTokenLike { function mint(address,uint) external; function burn(address,uint) external; } interface VatLike { function slip(bytes32,address,int) external; function move(address,address,uint) external; } /* Here we provide *adapters* to connect the Vat to arbitrary external token implementations, creating a bounded context for the Vat. The adapters here are provided as working examples: - `GemJoin`: For well behaved ERC20 tokens, with simple transfer semantics. - `ETHJoin`: For native ETH. - `UsddJoin`: For connecting internal Usdd balances to an external `DSToken` implementation. In practice, adapter implementations will be varied and specific to individual collateral types, accounting for different transfer semantics and token standards. Adapters need to implement two basic methods: - `join`: enter collateral into the system - `exit`: remove collateral from the system */ contract GemJoin { // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external auth { wards[usr] = 1; emit Rely(usr); } function deny(address usr) external auth { wards[usr] = 0; emit Deny(usr); } modifier auth { require(wards[msg.sender] == 1, "GemJoin/not-authorized"); _; } VatLike public vat; // CDP Engine bytes32 public ilk; // Collateral Type GemLike public gem; uint public dec; uint public live; // Active Flag // Events event Rely(address indexed usr); event Deny(address indexed usr); event Join(address indexed usr, uint256 wad); event Exit(address indexed usr, uint256 wad); event Cage(); constructor(address vat_, bytes32 ilk_, address gem_) public { wards[msg.sender] = 1; live = 1; vat = VatLike(vat_); ilk = ilk_; gem = GemLike(gem_); dec = gem.decimals(); emit Rely(msg.sender); } function cage() external auth { live = 0; emit Cage(); } function join(address usr, uint wad) external { require(live == 1, "GemJoin/not-live"); require(int(wad) >= 0, "GemJoin/overflow"); vat.slip(ilk, usr, int(wad)); require(gem.transferFrom(msg.sender, address(this), wad), "GemJoin/failed-transfer"); emit Join(usr, wad); } function exit(address usr, uint wad) external { require(wad <= 2 ** 255, "GemJoin/overflow"); vat.slip(ilk, msg.sender, -int(wad)); require(gem.transfer(usr, wad), "GemJoin/failed-transfer"); emit Exit(usr, wad); } } contract ETHJoin { // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external auth { wards[usr] = 1; emit Rely(usr); } function deny(address usr) external auth { wards[usr] = 0; emit Deny(usr); } modifier auth { require(wards[msg.sender] == 1, "ETHJoin/not-authorized"); _; } // Events event Rely(address indexed usr); event Deny(address indexed usr); event Join(address indexed usr, uint256 wad); event Exit(address indexed usr, uint256 wad); event Cage(); VatLike public vat; bytes32 public ilk; uint public live; // Access Flag constructor(address vat_, bytes32 ilk_) public { wards[msg.sender] = 1; live = 1; vat = VatLike(vat_); ilk = ilk_; } function cage() external auth { live = 0; emit Cage(); } function join(address usr) external payable { require(live == 1, "ETHJoin/not-live"); require(int(msg.value) >= 0, "ETHJoin/overflow"); vat.slip(ilk, usr, int(msg.value)); emit Join(usr, msg.value); } function exit(address payable usr, uint wad) external { require(int(wad) >= 0, "ETHJoin/overflow"); vat.slip(ilk, msg.sender, -int(wad)); usr.transfer(wad); emit Exit(usr, wad); } } contract UsddJoin { // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external auth { wards[usr] = 1; emit Rely(usr); } function deny(address usr) external auth { wards[usr] = 0; emit Deny(usr); } modifier auth { require(wards[msg.sender] == 1, "UsddJoin/not-authorized"); _; } VatLike public vat; // CDP Engine DSTokenLike public usdd; // Stablecoin Token uint public live; // Active Flag // Events event Rely(address indexed usr); event Deny(address indexed usr); event Join(address indexed usr, uint256 wad); event Exit(address indexed usr, uint256 wad); event Cage(); constructor(address vat_, address usdd_) public { wards[msg.sender] = 1; live = 1; vat = VatLike(vat_); usdd = DSTokenLike(usdd_); } function cage() external auth { live = 0; emit Cage(); } uint constant ONE = 10 ** 27; function mul(uint x, uint y) internal pure returns (uint z) { require(y == 0 || (z = x * y) / y == x); } function join(address usr, uint wad) external { vat.move(address(this), usr, mul(ONE, wad)); usdd.burn(msg.sender, wad); emit Join(usr, wad); } function exit(address usr, uint wad) external { require(live == 1, "UsddJoin/not-live"); vat.move(msg.sender, address(this), mul(ONE, wad)); usdd.mint(usr, wad); emit Exit(usr, wad); } }