입금 감시 워커에 새 토큰을 추가해야 해서 기존 분류에 넣고 돌렸더니 입금이 하나도 안 잡혔다.
Table of contents
Open Table of contents
증상 — 입금이 아예 안 잡혔다
기존 분류 집합에 새 토큰 하나를 더했다.
const ETH_LIKE_COINS = new Set([..., '<새 토큰>']);
ETH_LIKE_COINS 에 넣었더니 사용자가 보냈다는 txid 로 조회해도 이 응답이 돌아왔다.
해당 주소로 들어온 입금을 찾을 수 없습니다
값이 틀린 것이 아니라 대조 자체가 아예 안 걸리는 모양이었다.
검증 — 값을 대조했다
사용자가 준 txid 의 트랜잭션과 우리 입금 주소를 나란히 놓고 봤다.
트랜잭션의 to 0x…(토큰 컨트랙트 주소)
트랜잭션의 value 0
트랜잭션의 input 0xa9059cbb…(transfer 호출)
로그의 Transfer _to = 0x…(우리 입금 주소), _value = 토큰 수량
우리 주소는 to 가 아니라 로그의 _to 에 있었고 수량은 value 가 아니라 _value 에 있었다.
이 토큰을 ETH_LIKE_COINS 에 넣었으니 to 와 value 로 조회한다. 컨트랙트 주소와 0 을 보고 있었으니 결과가 나올 리가 없었다.
원인 — 필드에 든 다른 값
to 에 든 컨트랙트 주소가 무슨 값인지 따로 봤다.
토큰 전송은 코인을 보내는 것이 아니라 컨트랙트의 transfer 함수를 부르는 것이라 트랜잭션의 받는 쪽은 컨트랙트이고 보내는 코인은 없다. 받는 사람과 수량은 EIP-20 명세가 정한 대로 transfer 가 반드시 발생시켜야 하는 Transfer 이벤트의 _to 와 _value 에 실린다.
to 와 value 라는 이름이 내용을 보장하지 않는다는 것이 여기서 드러났다.
to / value
to 와 value 는 받는 주소와 금액이라는 뜻인데 코인 종류에 따라 다른 것이 들어 있었다.
ETH 만 볼 때는 다 같은 의미였겠지만 토큰이 늘면서 의미가 갈렸고 트랜잭션 구조를 바꿀 수는 없으니 같은 필드가 다형적이 됐다.
판단 기준 — 패턴이 셋
기존 코인들의 분류를 다시 정리해 보니 셋으로 갈렸다.
① UTXO 계열 블록의 출력(vout)에서 우리 주소와 금액을 읽음 — BTC·LTC
② 계정 계열 트랜잭션의 to 와 value 를 읽음 — ETH
③ 토큰 컨트랙트 로그의 Transfer 이벤트 _to 와 _value 를 읽음 — ERC20
새 토큰은 ③ 토큰 이었고 그 계열은 이벤트 하나가 입금 하나를 가리키는 1:1이다.
1:1 이라 그 뒤의 계산 쪽은 그대로 쓸 수 있었다. 블록 높이로 confirmations 를 세고 잔액에 반영하는 부분은 ETH 와 같고 조회 방법만 다른 것이다.
조치 — 고칠 데가 넷
분류를 정하고 나니 실제로 고쳐야 할 자리가 넷이었다.
① 집합에 추가 (TOKEN_COINS)
② 분류 분기
③ 전용 조회 구조 (eth_getLogs 에 컨트랙트 주소·Transfer 토픽 필터)
④ 전용 파싱 (_value 를 토큰의 decimals 로 나눔)
처음에 내가 한 것은 ETH_LIKE_COINS 에 넣은 것뿐이었다.
집합에만 넣으면 분기가 여전히 옛 경로를 타고 내려간다. 넷 중 하나라도 빠지면 어딘가에서 조용히 안 되는 상태가 된다. decimals 를 빠뜨리면 조회는 되는데 수량이 10의 몇 제곱 배로 틀리는 더 나쁜 상태가 된다.
재발 방지 — 신규 코인 절차
이 뒤로 신규 코인을 붙일 때의 절차를 적어 두었다.
1. 그 코인의 실제 입금 트랜잭션을 하나 뽑는다
2. 트랜잭션 필드와 로그를 나란히 놓는다
3. 우리 주소와 수량이 어디에 있는지 본다
값 대조 하나면 ① ② ③ 중 무엇인지가 그 자리에서 정해진다.
패턴을 안 정하고 집합부터 고치는 것 대신 대조부터 하는 쪽을 골랐다. 이유는 패턴이 정해지면 넷을 다 고치고 계산 쪽은 건드리지 않아도 돼서다. 새 코인을 붙이는 비용이 패턴을 정하는 것과 네 자리를 고치는 것으로 끝났다.
정리
- 코인마다 입금이 트랜잭션의 어느 자리에 실리는지 다르다
- UTXO 출력과 계정 전송과 컨트랙트 이벤트로 갈린다
- 잘못 분류하면 오류 없이 입금이 아예 안 잡힌다
to와value라는 필드 이름이 그 내용을 보장하지 않는다- 토큰 전송은
value가 0 이고 수량은Transfer이벤트에 있다 - 신규 코인은 실제 트랜잭션을 대조해 패턴을 먼저 정한다
- 분류를 정하면 집합과 분기와 조회 구조와 파싱 넷을 다 고친다
- 1:1 이면 확인 수·잔액 반영 로직은 재사용된다