TransactionReceipt
객체는 트랜잭션에 관한 정보와 디버깅을 돕는 다양한 메서드를 제공합니다.
>>> tx = Token[0].transfer(accounts[1], 1e18, {'from': accounts[0]})
Transaction sent: 0xa7616a96ef571f1791586f570017b37f4db9decb1a5f7888299a035653e8b44b
Token.transfer confirmed - block: 2 gas used: 51019 (33.78%)
>>> tx
<Transaction object '0xa7616a96ef571f1791586f570017b37f4db9decb1a5f7888299a035653e8b44b'>
트랜잭션에 대한 사람이 읽기 쉬운 정보를 보려면 TransactionReceipt.info
메서드를 호출하세요.
>>> tx.info()
Transaction was Mined
---------------------
Tx Hash: 0xa7616a96ef571f1791586f570017b37f4db9decb1a5f7888299a035653e8b44b
From: 0x4FE357AdBdB4C6C37164C54640851D6bff9296C8
To: 0xDd18d6475A7C71Ee33CEBE730a905DbBd89945a1
Value: 0
Function: Token.transfer
Block: 2
Gas Used: 51019 / 151019 (33.8%)
Events In This Transaction
--------------------------
Transfer
from: 0x4fe357adbdb4c6c37164c54640851d6bff9296c8
to: 0xfae9bc8a468ee0d8c84ec00c8345377710e0f0bb
value: 1000000000000000000
이벤트 데이터
이벤트에 관한 데이터는 TransactionReceipt.events
로 사용 가능합니다. 이 데이터는 EventDict
객체에 저장되며, 딕셔너리와 리스트 모두의 특성을 지닌 하이브리드 컨테이너입니다.
>>> tx.events
{
'CountryModified': [
{
'country': 1,
'limits': (0, 0, 0, 0, 0, 0, 0, 0),
'minrating': 1,
'permitted': True
},
{
'country': 2,
'limits': (0, 0, 0, 0, 0, 0, 0, 0),
'minrating': 1,
'permitted': True
}
],
'MultiSigCallApproved': [
{
'callHash': "0x0013ae2e37373648c5161d81ca78d84e599f6207ad689693d6e5938c3ae4031d",
'callSignature': "0xa513efa4",
'caller': "0xF9c1fd2f0452FA1c60B15f29cA3250DfcB1081b9",
'id': "0x8be1198d7f1848ebeddb3f807146ce7d26e63d3b6715f27697428ddb52db9b63"
}
]
}
일련의 발생 순서가 중요하지 않은 경우 특정 이벤트를 조회하는 데 사용할 수 있는 사전입니다:
>>> len(tx.events)
3
>>> len(tx.events['CountryModified'])
2
>>> 'MultiSigCallApproved' in tx.events
True
>>> tx.events['MultiSigCallApproved']
{
'callHash': "0x0013ae2e37373648c5161d81ca78d84e599f6207ad689693d6e5938c3ae4031d",
'callSignature': "0xa513efa4",
'caller': "0xF9c1fd2f0452FA1c60B15f29cA3250DfcB1081b9",
'id': "0x8be1198d7f1848ebeddb3f807146ce7d26e63d3b6715f27697428ddb52db9b63"
}
시퀀스가 중요한 경우나 동일한 이벤트가 둘 이상 발생한 경우 목록으로 표시합니다:
# name of the address>>> tx.events[1].name
'CountryModified'
# address where the event fired>>> tx.events[1].address
"0xDd18d6475A7C71Ee33CEBE730a905DbBd89945a1"
>>> tx.events[1]
{
'country': 1,
'limits': (0, 0, 0, 0, 0, 0, 0, 0),
'minrating': 1,
'permitted': True}
내부 거래 및 배포
TransactionReceipt.internal_transfers
은 트랜잭션 중 발생한 내부 이더 전송 목록을 제공합니다.
>>> tx.internal_transfers
[
{
"from": "0x79447c97b6543F6eFBC91613C655977806CB18b0",
"to": "0x21b42413bA931038f35e7A5224FaDb065d297Ba3",
"value": 100
}
]
TransactionReceipt.new_contracts
은 트랜잭션 중 생성된 새 계약 주소 목록을 제공합니다. 이는 팩토리 패턴을 사용할 때 유용합니다.
>>> deployer
<Deployer Contract object '0x5419710735c2D6c3e4db8F30EF2d361F70a4b380'>
>>> tx = deployer.deployNewContract()
Transaction sent: 0x6c3183e41670101c4ab5d732bfe385844815f67ae26d251c3bd175a28604da92
Gas price: 0.0 gwei Gas limit: 79781
Deployer.deployNewContract confirmed - Block: 4 Gas used: 79489 (99.63%)
>>> tx.new_contracts
["0x1262567B3e2e03f918875370636dE250f01C528c"]
이 목록에서 Contract
객체를 생성하려면 ContractContainer.at
을 사용하십시오:
>>> tx.new_contracts
["0x1262567B3e2e03f918875370636dE250f01C528c"]
>>> Token.at(tx.new_contracts[0])
<Token Contract object '0x1262567B3e2e03f918875370636dE250f01C528c'>
거래 실패 디버깅
RPCRequestError
를 일으킵니다.콘솔에서 트랜잭션이 revert되면 여전히 TransactionReceipt
가 반환되지만, revert됐다는 것이 표시됩니다. 오류 문자열이 제공되면 괄호 안에 표시되며 빨간색으로 강조됩니다.
>>> tx = Token[0].transfer(accounts[1], 1e18, {'from': accounts[3]})
Transaction sent: 0x5ff198f3a52250856f24792889b5251c120a9ecfb8d224549cb97c465c04262a
Token.transfer confirmed (Insufficient Balance) - block: 2 gas used: 23858 (19.26%)
<Transaction object '0x5ff198f3a52250856f24792889b5251c120a9ecfb8d224549cb97c465c04262a'>
에러 문자열은 TransactionReceipt.revert_msg
로도 사용할 수 있습니다.
>>> tx.revert_msg
'Insufficient Balance'
실패한 트랜잭션의 Python 스타일 트레이스백을 볼려면 TransactionReceipt.traceback
을 호출할 수 있습니다. 이는 revert로 이어지는 각 점프에서 소스 강조를 보여줍니다.
>>> tx.traceback()
Traceback for '0xd31c1c8db46a5bf2d3be822778c767e1b12e0257152fcc14dcf7e4a942793cb4':
Trace step 169, program counter 3659:
File "contracts/SecurityToken.sol", line 156, in SecurityToken.transfer:
_transfer(msg.sender, [msg.sender, _to], _value);
Trace step 5070, program counter 5666:
File "contracts/SecurityToken.sol", lines 230-234, in SecurityToken._transfer:
_addr = _checkTransfer(
_authID,
_id,
_addr
);
Trace step 5197, program counter 9719:
File "contracts/SecurityToken.sol", line 136, in SecurityToken._checkTransfer:
require(balances[_addr[SENDER]] >= _value, "Insufficient Balance");
트레이스 검사
트레이스 객체
트랜잭션에서 정확히 무엇이 일어났는지 이해하는 가장 좋은 방법은 트랜잭션 트레이스를 생성하고 검사하는 것입니다. 이는 리스트 형태의 사전 목록으로 사용 가능하며, 몇 가지 필드가 추가되어 이해하기 쉬워졌습니다.
트레이스의 각 단계에는 다음 데이터가 포함됩니다:
{
'address': "", // address of the contract containing this opcode
'contractName': "", // contract name
'depth': 0, // the number of external jumps away the initially called contract (starts at 0)
'error': "", // occurred error
'fn': "", // function name
'gas': 0, // remaining gas
'gasCost': 0, // cost to execute this opcode
'jumpDepth': 1, // number of internal jumps within the active contract (starts at 1)
'memory': [], // execution memory
'op': "", // opcode
'pc': 0, // program counter
'source': {
'filename': "path/to/file.sol", // path to contract source
'offset': [0, 0] // start:stop offset associated with this opcode
},
'stack': [], // execution stack
'storage': {} // contract storage
}
호출 추적
복잡한 거래를 다룰 때 추적은 수천 걸음으로 길어질 수 있습니다. 검토를 시작할 위치를 알기 어려울 수 있습니다. 브라우니는 거래에서 발생한 모든 점프의 전체 맵을 볼 수 있는 TransactionReceipt.call_trace
메소드를 제공합니다:
>>> tx.call_trace()
Call trace for '0x7824c6032966ca2349d6a14ec3174d48d546d0fb3020a71b08e50c7b31c1bcb1':
Initial call cost [21228 gas]
LiquidityGauge.deposit 0:3103 [64010 / 128030 gas]
├── LiquidityGauge._checkpoint 83:1826 [-6420 / 7698 gas]
│ ├── GaugeController.get_period_timestamp [STATICCALL] 119:384 [2511 gas]
│ ├── ERC20CRV.start_epoch_time_write [CALL] 411:499 [1832 gas]
│ ├── GaugeController.gauge_relative_weight_write [CALL] 529:1017 [3178 / 7190 gas]
│ │ └── GaugeController.change_epoch 697:953 [2180 / 4012 gas]
│ │ └── ERC20CRV.start_epoch_time_write [CALL] 718:806 [1832 gas]
│ └── GaugeController.period [STATICCALL] 1043:1336 [2585 gas]
├── LiquidityGauge._update_liquidity_limit 1929:2950 [45242 / 54376 gas]
│ ├── VotingEscrow.balanceOf [STATICCALL] 1957:2154 [2268 gas]
│ └── VotingEscrow.totalSupply [STATICCALL] 2180:2768 [6029 / 6866 gas]
│ └── VotingEscrow.supply_at 2493:2748 [837 gas]
└── ERC20LP.transferFrom [CALL] 2985:3098 [1946 gas]
각 줄에는 다음 정보가 표시됩니다:
ContractName.functionName (external call opcode) start:stop [internal / total gas used]
여기서 start
와 stop
은 함수가 입력되고 종료된 TransactionReceipt.trace
의 인덱스입니다. TransactionReceipt.call_trace
는 트랜잭션 실행 경로의 초기 고수준 개요를 제공하여 복잡한 트랜잭션에서 문제가 발생한 곳을 보다 명확하게 확인할 수 있도록 개별 추적 단계를 검사하는 데 도움이 됩니다.
REVERT
또는 INVALID
옵코드로 종료된 함수는 빨간색으로 표시됩니다.
하위 호출이 없는 함수의 경우 사용된 가스가 표시됩니다. 그렇지 않으면 첫 번째 가스 숫자는 이 함수에서 내부적으로 사용된 가스 양이며 두 번째 숫자는 하위 호출을 포함한 함수에서 사용된 총 가스입니다. 스토리지 또는 계약을 삭제하여 발생하는 가스 환불은 음수 가스 사용으로 표시됩니다. 기존의 0 값에 다른 0 값을 덮어 쓰면 가스 환불이 잘못 표시됩니다.
True
를 인수로 사용하여 TransactionReceipt.call_trace
를 호출하면 확장된 보기를 제공합니다:
>>> history[-1].call_trace(True)
Call trace for '0x7824c6032966ca2349d6a14ec3174d48d546d0fb3020a71b08e50c7b31c1bcb1':
Initial call cost [21228 gas]
LiquidityGauge.deposit 0:3103 [64010 / 128030 gas]
├── LiquidityGauge._checkpoint 83:1826 [-6420 / 7698 gas]
│ │
│ ├── GaugeController.get_period_timestamp [STATICCALL] 119:384 [2511 gas]
│ │ ├── address: 0x0C41Fc429cC21BC3c826efB3963929AEdf1DBb8e
│ │ ├── input arguments:
│ │ │ └── p: 0
│ │ └── return value: 1594574319
...
확장된 추적에는 다음과 같은 외부 하위 호출에 관한 정보가 포함됩니다:
- 대상 주소
- 전송된 이더량
- 입력 인수
- 반환 값
되돌아가는 호출의 경우 반환 값 대신 되돌아가는 이유가 제공됩니다:
>>> history[-1].call_trace(True)
...└── ERC20LP.transferFrom [CALL] 2985:3098 [1946 gas]
├── address: 0xd495633B90a237de510B4375c442C0469D3C161C
├── value: 0
├── input arguments:
│ ├── _from: 0x9EC9431CCCCD2C73F0A2F68DC69A4A527AB5D809
│ ├── _to: 0x5AE569698C5F986665018B6E1D92A71BE71DEF9A
│ └── _value: 100000
└── revert reason: Integer underflow
이 정보는 TransactionReceipt.subcalls
속성을 통해 프로그램적으로도 접근할 수 있습니다:
>>> history[-1].subcalls
[
{
'from': "0x5AE569698C5F986665018B6e1d92A71be71DEF9a",
'function': "get_period_timestamp(int128)",
'inputs': {
'p': 0
},
'op': "STATICCALL",
'return_value': (1594574319,),
'to': "0x0C41Fc429cC21BC3c826efB3963929AEdf1DBb8e"
},
...
'블록체인 (Block Chain) > 이더리움' 카테고리의 다른 글
[브라우니 (Brownie)] 15. 단위 테스트 작성하기 (0) | 2023.06.18 |
---|---|
[브라우니 (Brownie)] 14. 데이터 유형 (0) | 2023.06.16 |
[브라우니 (Brownie)] 12. 블록체인과 상호작용하기 (0) | 2023.06.16 |
[브라우니 (Brownie)] 11. 트랜잭션 가스 가격 설정하기 (0) | 2023.06.16 |
[브라우니 (Brownie)] 10. 컨트랙트로 작업하기 (0) | 2023.06.16 |