pragma solidity ^0.4.24; //Primero defino las funciones utilizadas para hacer el contrato ERC20. contract SafeMath { function safeAdd(uint a, uint b) public pure returns (uint c) { c = a + b; require(c >= a); } function safeSub(uint a, uint b) public pure returns (uint c) { require(b <= a); c = a - b; } function safeMul(uint a, uint b) public pure returns (uint c) { c = a * b; require(a == 0 || c / a == b); } function safeDiv(uint a, uint b) public pure returns (uint c) { require(b > 0); c = a / b; } } contract ERC20Interface { function totalSupply() public constant returns (uint); function balanceOf(address tokenOwner) public constant returns (uint balance); function allowance(address tokenOwner, address spender) public constant returns (uint remaining); function transfer(address to, uint tokens) public returns (bool success); function approve(address spender, uint tokens) public returns (bool success); function transferFrom(address from, address to, uint tokens) public returns (bool success); event Transfer(address indexed from, address indexed to, uint tokens); event Approval(address indexed tokenOwner, address indexed spender, uint tokens); } //COMIENZO DE SMART BOND contract SmartBond is SafeMath, ERC20Interface { address public CNV; //La Comisión Nacional de Valores ("CNV") cumple la función de dueño del contrato. address public CVSA; //La Caja de Valores S.A. ("CVSA") cumple la función de oráculo y además se encarga de los cobros y pagos, tanto en moneda FIAT como en criptomoneda. address public emisor; //El emisor de la deuda carga la información relativa a las condiciones del bono uint public etapa = 0; //En la etapa 0 la CNV setea los actores recien definidos constructor () public { CNV = msg.sender; } function setCVSA(address _CVSA) public { require(msg.sender == CNV); CVSA = _CVSA; } function setEmisor(address _emisor) public { require(msg.sender == CNV); emisor = _emisor; } //La siguiente funcion es ejecutada por el oráculo (CVSA) a lo largo de la vida del contrato para pasar de etapa. Cada etapa tiene distintos requerimientos. function proximaEtapa() public { require(msg.sender == CVSA); if (etapa == 0) { require(emisor != address(0)); //Se chquea que el emisor este seteado. Al estar seteados los 3 actores principales se procede a la etapa 1. etapa = safeAdd(etapa,1); } else if (etapa == 1) { require(emisionAprobada == true); //Para pasar a la segunda etapa la emision debe estar aprobada. etapa = safeAdd(etapa,1); } else if (etapa == 2) { require(superaMin == true); //Para pasar a la tercera etapa, la emision tiene que superar el minimo requerido. etapa = safeAdd(etapa,1); } } //ETAPA 1 //Propuesta y estructuración de emision: aquí se detallan las condiciones de la emision y posteriormente la CNV (owner y regulador) la aprueba. string prospectoEmision; //Documentos con las condiciones de la emision y extras, se incluye en el contrato para dar más transparencia string simboloToken; //Simbolo del token string nombreToken; //Nombre del token uint256 cantidadMin; //Cantidad minima de bonos de la emision uint256 cantidadMax; //Cantidad maxima de bonos de la emision uint256 precioEmision; //Precio orientativo de la emision uint256 fechaEmision; //Timestamp de emision uint256 fechaVencimiento; //Timestamp de vencimiento struct flujo { uint256 timestamp; uint256 proporcionPago; } uint256 indiceFlujo; //Indice utilizado en el siguiente mapping. mapping (uint256 => flujo) flujoFondos; //Mapping de indice a struct con las fechas en timestamp y sus respectivas proporciones de pagos por token. bool emisionAprobada = false; //El regulador aprubea la emision si identifica que las condiciones de la misma son aceptables, pasa la variable a true. //En esta funcion el emisor carga todos los datos de la emision, excepto por el el flujo de fondos (calendario de pagos) function cargarDatosEmision(string _prospectoEmision, uint256 _cantidadMin, uint256 _cantidadMax, uint256 _precioEmision, uint256 _fechaEmision, uint256 _fechaVencimiento) public { require(msg.sender == emisor); //Sólo el emisor puede incorporar esta información. require(etapa == 1); //Requiere que le etapa sea la primera, es decir, la de estructuración de la emision. prospectoEmision = _prospectoEmision; cantidadMin = _cantidadMin; cantidadMax = _cantidadMax; precioEmision = _precioEmision; fechaEmision = _fechaEmision; fechaVencimiento = _fechaVencimiento; } //El emisor carga uno a uno los flujos en el mapping. El resultado es un mapping con un indice incremental de pagos, y el struct de flujo seteado. function cargarFlujoFondos(uint256 _indiceFlujo, uint256 _timestamp, uint256 _proporcionPago) public { require(msg.sender == emisor); require(etapa == 1); flujo memory temporario; temporario.timestamp = _timestamp; temporario.proporcionPago = _proporcionPago; flujoFondos[_indiceFlujo] = temporario; indiceFlujo = safeAdd(indiceFlujo,1); } //El regulador aprueba o desaprueba la emision, y en caso de desaprobarla el emisor debe volver a ejecutar la funcion cargarDatosEmision y cargarFlujoFondos function aprobarEmision(bool _aprueba) public { require(msg.sender == CNV); //Requiere al regulador para aprobar. require(etapa == 1); //Requiere que esto transcurra en la primera etapa. emisionAprobada = _aprueba; //Aprueba la emisio. if (_aprueba = false) { indiceFlujo = 0; //Si no aprueba, reinicia el contador del mapping para que pueda cargarse de nuevo por el emisor. } } //FIN ETAPA 1 //ETAPA 2: //En la segunda etapa CVSA asigna las ofertas que son exitosas en la subasta y esto impacta en la asignación de tokens a los inversores. mapping (address => uint) contadores; //Este mapping va del address de los tenedores de bonos hacia un numero que representa el número de pago. struct inversor{ uint256 cantidad; //Cuantos tokens (bonos) tiene el inversor uint256 precio; //Precio al cual hizo la oferta de suscripcion (el precio final puede ser menor, se le devuelve la diferencia offchain) uint256 contador; //Se usa para contabilizar los pagos ya realizados } mapping (address => inversor) inversores; uint public cantidadEmision = 0; uint256 public precioSubasta; //Precio final de la subasta será el precio minimo que ingrese en la cantidad de tokens a subastar bool public superaMin = false; //Para poder realizar la emision, y naturalmente pasar a la siguiente etapa, se requiere que pase el monto minimo bool public superaMax = false; //En caso de superar el maximo de tokens subscriptos, frena la asignacion de ofertas y asigna el remanente posible al inversor actual //Caja carga las ofertas en orden de precio decreciente function asignarOfertas (address _inversor, uint256 _cantidad, uint256 _precio) public { require(msg.sender == CVSA && etapa == 2 && superaMax == false); //Requiere que sea Caja, que la etapa sea la 2 y que no haya superado el máximo if (_precio <= precioSubasta){ precioSubasta = _precio; } //La lógica siguiente setea el mapping balances con la cantidad de tokens correspondientes y chequea que se cumplan con los minimos y maximos de suscripcion cantidadEmision = cantidadEmision + _cantidad; _totalSupply = cantidadEmision; if (cantidadEmision >= cantidadMin) { superaMin = true; } inversor memory temporario; if (cantidadEmision <= cantidadMax) { temporario.cantidad = _cantidad; balances[_inversor] = _cantidad; allowed[_inversor][CVSA] = _cantidad; } else { temporario.cantidad = _cantidad + cantidadMax - cantidadEmision; cantidadEmision = cantidadEmision + temporario.cantidad - _cantidad; _totalSupply = cantidadEmision; balances[_inversor] = cantidadEmision + temporario.cantidad - _cantidad; allowed[_inversor][CVSA] = _cantidad; superaMax = true; //Al superar el maximo, setea la variable en true y no deja ingresar mas ofertas } temporario.precio = _precio; //Setea el precio para poder devolver el excedente de la suscripcion, en caso de haber inversores[_inversor] = temporario; } //FIN ETAPA 2 //ETAPA 3 //La tercera etapa permite a los tenedores de tokens intercambiarlos mediante la interfase ERC20, y también cobrarse los pagos correspondientes //Se definen variables para hacer el contrato ERC20 bool public esERC20 = false; string public symbol; string public name; uint8 public decimals; uint public _totalSupply; mapping(address => uint) balances; mapping(address => mapping(address => uint)) allowed; //Permito a CVSA poder retirar fondos de las cuentas por si hay algun inconveniente. function hacerERC20() public { require(msg.sender == CVSA); //Caja es el encargado de convertirlo symbol = simboloToken; name = nombreToken; decimals = 2; _totalSupply = cantidadEmision; esERC20 = true; } //Las siguientes funciones definen las funcionalidades para la transferencia de tokens, se agrega una validación extra debido a la naturaleza del problema. function totalSupply() public constant returns (uint) { return _totalSupply - balances[address(0)]; } function balanceOf(address tokenOwner) public constant returns (uint balance) { return balances[tokenOwner]; } function transfer(address to, uint tokens) public returns (bool success) { //El siguiente require agrega el control de que deben estar en el mismo pago para transaccionar tokens de bonos, o que el que recibe no tiene bonos, por lo que luego le setea el numero de pago correspondiente (en el if consecutivo) require(contadores[msg.sender] == contadores[to] || balances[to] == 0); if(balances[to] == 0) { contadores[to] = contadores[msg.sender]; } balances[msg.sender] = safeSub(balances[msg.sender], tokens); balances[to] = safeAdd(balances[to], tokens); emit Transfer(msg.sender, to, tokens); return true; } function approve(address spender, uint tokens) public returns (bool success) { allowed[msg.sender][spender] = tokens; emit Approval(msg.sender, spender, tokens); return true; } function transferFrom(address from, address to, uint tokens) public returns (bool success) { //El siguiente require agrega el control de que deben estar en el mismo pago para transaccionar tokens de bonos, o que el que recibe no tiene bonos, por lo que luego le setea el numero de pago correspondiente (en el if consecutivo) require(contadores[from] == contadores[to] || balances[to] == 0); if(balances[to] == 0) { contadores[to] = contadores[from]; } balances[from] = safeSub(balances[from], tokens); allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens); balances[to] = safeAdd(balances[to], tokens); emit Transfer(from, to, tokens); return true; } function allowance(address tokenOwner, address spender) public constant returns (uint remaining) { return allowed[tokenOwner][spender]; } function () public payable { //revert(); //No hace falta, le permito recibir tokens } //Finalizan las funciones de ERC20 //El contrato cuenta con una funcion para transferir cualquier token ERC20 que sea recibido por error ya que no puede detener tokens entrantes, y para eso lo transfiere a Caja de Valores function transferAnyERC20Token(address tokenAddress, uint tokens) public returns (bool success) { require(msg.sender == CVSA); return ERC20Interface(tokenAddress).transfer(CVSA, tokens); } //Direccion del contrato de DAI address public contratoDAI = 0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359; //Si el contrato de DAI cambia, CVSA tiene la posibilidad de modificar esta direccion function setDAI(address _DAI) public { require(msg.sender == CVSA); contratoDAI = _DAI; } //La funcion de cobro permite a los tenedores de tokens cobrar en DAI lo que le corresponde en su cuenta de Ethereum function cobro(address _inversor, uint _numeroPago) public { //Se requiere estar en la tercera etapa, que sea ERC20, que tenga balance el inversor, que sea el pago que le corresponda cobrar en el momento que le corresponda y que no haya vencido require(etapa == 3); require(esERC20 == true); require(balances[_inversor] > 0); require(contadores[_inversor] == _numeroPago); require(flujoFondos[_numeroPago].timestamp < now); require(fechaVencimiento > now); uint pago = safeMul(flujoFondos[_numeroPago].proporcionPago,balances[_inversor]); //El pago corresponde a la cantidad de tokens multiplicado por la proporcion de ese pago. //Solicita una transferencia del address de Caja en el contrato de DAI hacia el tenedor de tokens. (bool success) = contratoDAI.call(bytes4(keccak256("transferFrom(address,address,uint)")),CVSA,_inversor,pago); //Si la transferencia es exitosa procede a pasar al siguiente contador de pago para el address que solicito el pago require(success); //Pasa al siguiente pago, por lo que hasta la proxima fecha no puede solicitar el pago contadores[_inversor] == safeAdd(contadores[_inversor],1); } //FIN ETAPA 3 }
0.4.18